From: Samual Lenks Date: Tue, 4 Dec 2012 20:44:39 +0000 (-0500) Subject: Merge remote-tracking branch 'origin/master' into terencehill/cursormode X-Git-Tag: xonotic-v0.7.0~196^2~1 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=fcbf9538330960b989dbe84e7188349d7e8b109f;hp=4524af86ee6190c8d7f7cd7ccc068650809a0ce7 Merge remote-tracking branch 'origin/master' into terencehill/cursormode --- diff --git a/_hud_descriptions.cfg b/_hud_descriptions.cfg index 372a6af3e1..cb95f96813 100644 --- a/_hud_descriptions.cfg +++ b/_hud_descriptions.cfg @@ -206,6 +206,7 @@ seta hud_panel_pressedkeys_bg_alpha "" "if set to something else than \"\" = ove seta hud_panel_pressedkeys_bg_border "" "if set to something else than \"\" = override default size of border around the background" seta hud_panel_pressedkeys_bg_padding "" "if set to something else than \"\" = override default padding of contents from border" seta hud_panel_pressedkeys_aspect "" "forced aspect on panel" +seta hud_panel_pressedkeys_attack "" "show attack buttons too" seta hud_panel_chat "" "enable/disable this panel" seta hud_panel_chat_pos "" "position of this base of the panel" diff --git a/balance25.cfg b/balance25.cfg index 51bb152dde..068f650f8f 100644 --- a/balance25.cfg +++ b/balance25.cfg @@ -5,7 +5,6 @@ set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide th set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minelayer -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" @@ -14,11 +13,8 @@ set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always p set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hlac -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_rifle -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_seeker -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_balance_health_start 150 set g_balance_armor_start 0 set g_start_ammo_shells 40 @@ -162,17 +158,16 @@ set g_throughfloor_force 1 set g_projectiles_damage 2 // possible values: // -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, minelayer mines) +// -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) -// 3: absolute velocity + player velocity component in shot direction (note: does NOT yield the right relative velocity, but may be good enough, but it is somewhat prone to sniper rockets) -// 4: just add the player velocity length to the absolute velocity (tZork's sniper rockets) set g_projectiles_newton_style_2_minfactor 0.7 set g_projectiles_newton_style_2_maxfactor 5 set g_projectiles_spread_style 0 @@ -374,31 +369,6 @@ set g_balance_grenadelauncher_bouncestop 0.075 set g_balance_grenadelauncher_reload_ammo 0 //default: 12 set g_balance_grenadelauncher_reload_time 2 // }}} -// {{{ minelayer -set g_balance_minelayer_damage 35 -set g_balance_minelayer_edgedamage 30 -set g_balance_minelayer_force 250 -set g_balance_minelayer_radius 175 -set g_balance_minelayer_proximityradius 150 -set g_balance_minelayer_speed 750 -set g_balance_minelayer_lifetime 60 -set g_balance_minelayer_lifetime_countdown 0 -set g_balance_minelayer_refire 1.5 -set g_balance_minelayer_animtime 0.4 -set g_balance_minelayer_ammo 5 -set g_balance_minelayer_health 15 -set g_balance_minelayer_limit 4 // 0 disables the limit -set g_balance_minelayer_protection 1 // don't explode if the mine would hurt the owner or a team mate -set g_balance_minelayer_damageforcescale 0 -set g_balance_minelayer_detonatedelay -1 // 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_minelayer_time 0.5 -set g_balance_minelayer_remote_damage 45 -set g_balance_minelayer_remote_edgedamage 40 -set g_balance_minelayer_remote_radius 200 -set g_balance_minelayer_remote_force 300 -set g_balance_minelayer_reload_ammo 0 //default: 15 -set g_balance_minelayer_reload_time 2 -// }}} // {{{ electro set g_balance_electro_lightning 0 set g_balance_electro_primary_damage 65 @@ -481,6 +451,7 @@ set g_balance_crylink_secondary_force -55 set g_balance_crylink_secondary_radius 3 set g_balance_crylink_secondary_speed 7000 set g_balance_crylink_secondary_spread 0.08 +set g_balance_crylink_secondary_spreadtype 0 set g_balance_crylink_secondary_shots 7 set g_balance_crylink_secondary_bounces 0 set g_balance_crylink_secondary_refire 0.5 @@ -659,77 +630,6 @@ set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds set g_balance_hook_secondary_health 0 set g_balance_hook_secondary_damageforcescale 0 // }}} -// {{{ hlac -set g_balance_hlac_primary_spread_min 0.01 -set g_balance_hlac_primary_spread_max 0.25 -set g_balance_hlac_primary_spread_add 0.0045 -set g_balance_hlac_primary_spread_crouchmod 0.25 - -set g_balance_hlac_primary_damage 23 -set g_balance_hlac_primary_edgedamage 10 -set g_balance_hlac_primary_force 100 -set g_balance_hlac_primary_radius 70 -set g_balance_hlac_primary_speed 9000 -set g_balance_hlac_primary_lifetime 5 - -set g_balance_hlac_primary_refire 0.1 -set g_balance_hlac_primary_animtime 0.4 -set g_balance_hlac_primary_ammo 1 - -set g_balance_hlac_secondary 1 -set g_balance_hlac_secondary_spread 0.15 -set g_balance_hlac_secondary_spread_crouchmod 0.5 - -set g_balance_hlac_secondary_damage 23 -set g_balance_hlac_secondary_edgedamage 10 -set g_balance_hlac_secondary_force 100 -set g_balance_hlac_secondary_radius 70 -set g_balance_hlac_secondary_speed 9000 -set g_balance_hlac_secondary_lifetime 5 - -set g_balance_hlac_secondary_refire 1 -set g_balance_hlac_secondary_animtime 0.3 -set g_balance_hlac_secondary_ammo 10 -set g_balance_hlac_secondary_shots 6 - -set g_balance_hlac_reload_ammo 0 //default: 20 -set g_balance_hlac_reload_time 2 -// }}} -// {{{ rifle -set g_balance_rifle_bursttime 0 -set g_balance_rifle_primary_tracer 0 -set g_balance_rifle_primary_shots 1 -set g_balance_rifle_primary_damage 60 -set g_balance_rifle_primary_headshotaddeddamage 100 -set g_balance_rifle_primary_spread 0 -set g_balance_rifle_primary_force 2 -set g_balance_rifle_primary_speed 35000 -set g_balance_rifle_primary_lifetime 5 -set g_balance_rifle_primary_refire 0.8 -set g_balance_rifle_primary_animtime 0.3 -set g_balance_rifle_primary_ammo 10 -set g_balance_rifle_primary_bulletconstant 130 // 56.3qu -set g_balance_rifle_primary_burstcost 0 -set g_balance_rifle_primary_bullethail 0 // empty magazine on shot -set g_balance_rifle_secondary 1 -set g_balance_rifle_secondary_reload 0 -set g_balance_rifle_secondary_tracer 0 -set g_balance_rifle_secondary_shots 1 -set g_balance_rifle_secondary_damage 35 -set g_balance_rifle_secondary_headshotaddeddamage 15 // 50 damage only on head -set g_balance_rifle_secondary_spread 0.008 -set g_balance_rifle_secondary_force 1 -set g_balance_rifle_secondary_speed 20000 -set g_balance_rifle_secondary_lifetime 5 -set g_balance_rifle_secondary_refire 0.15 -set g_balance_rifle_secondary_animtime 0.1 -set g_balance_rifle_secondary_ammo 10 -set g_balance_rifle_secondary_bulletconstant 130 // 18.3qu -set g_balance_rifle_secondary_burstcost 0 -set g_balance_rifle_secondary_bullethail 0 // empty magazine on shot -set g_balance_rifle_reload_ammo 80 //default: 80 -set g_balance_rifle_reload_time 2 -// }}} // {{{ tuba set g_balance_tuba_refire 0.05 set g_balance_tuba_animtime 0.05 @@ -778,57 +678,3 @@ set g_balance_fireball_secondary_speed_up 100 set g_balance_fireball_secondary_speed_z 0 set g_balance_fireball_secondary_spread 0 // }}} -// {{{ seeker -set g_balance_seeker_type 0 // 0 = old seeker, 1 = new seeker. THIS IS A TEMPORARY CVAR FOR TESTING, will be removed later. -set g_balance_seeker_flac_ammo 0.5 -set g_balance_seeker_flac_animtime 0.1 -set g_balance_seeker_flac_damage 15 -set g_balance_seeker_flac_edgedamage 10 -set g_balance_seeker_flac_force 50 -set g_balance_seeker_flac_lifetime 0.1 -set g_balance_seeker_flac_lifetime_rand 0.05 -set g_balance_seeker_flac_radius 100 -set g_balance_seeker_flac_refire 0.1 -set g_balance_seeker_flac_speed 3000 -set g_balance_seeker_flac_speed_up 1000 -set g_balance_seeker_flac_speed_z 0 -set g_balance_seeker_flac_spread 0.4 -set g_balance_seeker_missile_accel 3000 -set g_balance_seeker_missile_ammo 2 -set g_balance_seeker_missile_animtime 0.3 -set g_balance_seeker_missile_count 4 -set g_balance_seeker_missile_damage 40 -set g_balance_seeker_missile_damageforcescale 4 -set g_balance_seeker_missile_decel 6000 -set g_balance_seeker_missile_delay 0.25 -set g_balance_seeker_missile_edgedamage 10 -set g_balance_seeker_missile_force 250 -set g_balance_seeker_missile_health 5 -set g_balance_seeker_missile_lifetime 15 -set g_balance_seeker_missile_proxy 0 -set g_balance_seeker_missile_proxy_delay 0.2 -set g_balance_seeker_missile_proxy_maxrange 45 -set g_balance_seeker_missile_radius 80 -set g_balance_seeker_missile_refire 0.5 -set g_balance_seeker_missile_smart 1 -set g_balance_seeker_missile_smart_mindist 800 -set g_balance_seeker_missile_smart_trace_max 2500 -set g_balance_seeker_missile_smart_trace_min 1000 -set g_balance_seeker_missile_speed 700 -set g_balance_seeker_missile_speed_up 300 -set g_balance_seeker_missile_speed_z 0 -set g_balance_seeker_missile_speed_max 1250 -set g_balance_seeker_missile_spread 0 -set g_balance_seeker_missile_turnrate 0.65 -set g_balance_seeker_tag_ammo 1 -set g_balance_seeker_tag_animtime 0.3 -set g_balance_seeker_tag_damageforcescale 4 -set g_balance_seeker_tag_health 5 -set g_balance_seeker_tag_lifetime 15 -set g_balance_seeker_tag_refire 0.7 -set g_balance_seeker_tag_speed 9000 -set g_balance_seeker_tag_spread 0 -set g_balance_seeker_tag_tracker_lifetime 10 -set g_balance_seeker_reload_ammo 0 //default: 15 -set g_balance_seeker_reload_time 2 -// End new seeker diff --git a/balanceFruitieX.cfg b/balanceFruitieX.cfg index ced09e7d10..5ccd6827b6 100644 --- a/balanceFruitieX.cfg +++ b/balanceFruitieX.cfg @@ -5,7 +5,6 @@ set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide th set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minelayer -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" @@ -14,11 +13,8 @@ set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always p set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hlac -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_rifle -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_seeker -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_balance_health_start 125 set g_balance_armor_start 0 set g_start_ammo_shells 20 @@ -162,17 +158,16 @@ set g_throughfloor_force 0.8 set g_projectiles_damage 2 // possible values: // -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, minelayer mines) +// -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) -// 3: absolute velocity + player velocity component in shot direction (note: does NOT yield the right relative velocity, but may be good enough, but it is somewhat prone to sniper rockets) -// 4: just add the player velocity length to the absolute velocity (tZork's sniper rockets) set g_projectiles_newton_style_2_minfactor 0.7 set g_projectiles_newton_style_2_maxfactor 5 set g_projectiles_spread_style 7 @@ -374,31 +369,6 @@ set g_balance_grenadelauncher_bouncestop 0.12 set g_balance_grenadelauncher_reload_ammo 0 //default: 12 set g_balance_grenadelauncher_reload_time 2 // }}} -// {{{ minelayer -set g_balance_minelayer_damage 42 -set g_balance_minelayer_edgedamage 30 -set g_balance_minelayer_force 250 -set g_balance_minelayer_radius 175 -set g_balance_minelayer_proximityradius 150 -set g_balance_minelayer_speed 750 -set g_balance_minelayer_lifetime 60 -set g_balance_minelayer_lifetime_countdown 0 -set g_balance_minelayer_refire 1.5 -set g_balance_minelayer_animtime 0.3 -set g_balance_minelayer_ammo 5 -set g_balance_minelayer_health 15 -set g_balance_minelayer_limit 3 // 0 disables the limit // LOG: 4 -> 3 -set g_balance_minelayer_protection 1 // don't explode if the mine would hurt the owner or a team mate -set g_balance_minelayer_damageforcescale 0 -set g_balance_minelayer_detonatedelay -1 // 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_minelayer_time 0.5 -set g_balance_minelayer_remote_damage 45 -set g_balance_minelayer_remote_edgedamage 40 -set g_balance_minelayer_remote_radius 200 -set g_balance_minelayer_remote_force 300 -set g_balance_minelayer_reload_ammo 0 //default: 15 -set g_balance_minelayer_reload_time 2 -// }}} // {{{ electro set g_balance_electro_lightning 1 set g_balance_electro_primary_damage 100 @@ -481,6 +451,7 @@ set g_balance_crylink_secondary_force 16 // LOG: 20 -> 16 set g_balance_crylink_secondary_radius 15 // LOG: 20 -> 15 set g_balance_crylink_secondary_speed 1250 // LOG: 1500 -> 1250 set g_balance_crylink_secondary_spread 0.1 +set g_balance_crylink_secondary_spreadtype 0 set g_balance_crylink_secondary_shots 6 set g_balance_crylink_secondary_bounces 2 set g_balance_crylink_secondary_refire 0.9 // LOG: 0.8 -> 0.9 @@ -659,77 +630,6 @@ set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds set g_balance_hook_secondary_health 0 set g_balance_hook_secondary_damageforcescale 0 // }}} -// {{{ hlac -set g_balance_hlac_primary_spread_min 0.01 -set g_balance_hlac_primary_spread_max 0.075 -set g_balance_hlac_primary_spread_add 0.001 -set g_balance_hlac_primary_spread_crouchmod 0.25 - -set g_balance_hlac_primary_damage 15 -set g_balance_hlac_primary_edgedamage 10 -set g_balance_hlac_primary_force 70 -set g_balance_hlac_primary_radius 40 -set g_balance_hlac_primary_speed 9000 -set g_balance_hlac_primary_lifetime 5 - -set g_balance_hlac_primary_refire 0.1 -set g_balance_hlac_primary_animtime 0.2 -set g_balance_hlac_primary_ammo 1 - -set g_balance_hlac_secondary 1 -set g_balance_hlac_secondary_spread 0.15 -set g_balance_hlac_secondary_spread_crouchmod 0.5 - -set g_balance_hlac_secondary_damage 20 -set g_balance_hlac_secondary_edgedamage 13 -set g_balance_hlac_secondary_force 100 -set g_balance_hlac_secondary_radius 45 -set g_balance_hlac_secondary_speed 9000 -set g_balance_hlac_secondary_lifetime 5 - -set g_balance_hlac_secondary_refire 0.8 -set g_balance_hlac_secondary_animtime 0.4 -set g_balance_hlac_secondary_ammo 4 -set g_balance_hlac_secondary_shots 6 - -set g_balance_hlac_reload_ammo 0 //default: 20 -set g_balance_hlac_reload_time 2 -// }}} -// {{{ rifle -set g_balance_rifle_bursttime 0 -set g_balance_rifle_primary_tracer 1 -set g_balance_rifle_primary_shots 1 -set g_balance_rifle_primary_damage 60 -set g_balance_rifle_primary_headshotaddeddamage 60 -set g_balance_rifle_primary_spread 0 -set g_balance_rifle_primary_force 2 -set g_balance_rifle_primary_speed 40000 -set g_balance_rifle_primary_lifetime 5 -set g_balance_rifle_primary_refire 1.5 -set g_balance_rifle_primary_animtime 1.4 -set g_balance_rifle_primary_ammo 10 -set g_balance_rifle_primary_bulletconstant 110 // 62.2qu -set g_balance_rifle_primary_burstcost 0 -set g_balance_rifle_primary_bullethail 0 // empty magazine on shot -set g_balance_rifle_secondary 1 -set g_balance_rifle_secondary_reload 1 -set g_balance_rifle_secondary_tracer 0 -set g_balance_rifle_secondary_shots 1 -set g_balance_rifle_secondary_damage 42 -set g_balance_rifle_secondary_headshotaddeddamage 42 -set g_balance_rifle_secondary_spread 0 -set g_balance_rifle_secondary_force 2 -set g_balance_rifle_secondary_speed 20000 -set g_balance_rifle_secondary_lifetime 5 -set g_balance_rifle_secondary_refire 1.5 -set g_balance_rifle_secondary_animtime 1.4 -set g_balance_rifle_secondary_ammo 10 -set g_balance_rifle_secondary_bulletconstant 110 // 15.5qu -set g_balance_rifle_secondary_burstcost 0 -set g_balance_rifle_secondary_bullethail 0 // empty magazine on shot -set g_balance_rifle_reload_ammo 80 //default: 80 -set g_balance_rifle_reload_time 2 -// }}} // {{{ tuba set g_balance_tuba_refire 0.05 set g_balance_tuba_animtime 0.05 @@ -778,57 +678,3 @@ set g_balance_fireball_secondary_speed_up 100 set g_balance_fireball_secondary_speed_z 0 set g_balance_fireball_secondary_spread 0 // }}} -// {{{ seeker -set g_balance_seeker_type 0 // 0 = old seeker, 1 = new seeker. THIS IS A TEMPORARY CVAR FOR TESTING, will be removed later. -set g_balance_seeker_flac_ammo 0.5 -set g_balance_seeker_flac_animtime 0.1 -set g_balance_seeker_flac_damage 15 -set g_balance_seeker_flac_edgedamage 10 -set g_balance_seeker_flac_force 50 -set g_balance_seeker_flac_lifetime 0.1 -set g_balance_seeker_flac_lifetime_rand 0.05 -set g_balance_seeker_flac_radius 100 -set g_balance_seeker_flac_refire 0.1 -set g_balance_seeker_flac_speed 3000 -set g_balance_seeker_flac_speed_up 1000 -set g_balance_seeker_flac_speed_z 0 -set g_balance_seeker_flac_spread 0.4 -set g_balance_seeker_missile_accel 1400 -set g_balance_seeker_missile_ammo 2 -set g_balance_seeker_missile_animtime 0.2 -set g_balance_seeker_missile_count 3 // LOG: 8 -> 3 -set g_balance_seeker_missile_damage 30 // LOG: 15 -> 30 -set g_balance_seeker_missile_damageforcescale 4 -set g_balance_seeker_missile_decel 1400 -set g_balance_seeker_missile_delay 0.25 -set g_balance_seeker_missile_edgedamage 10 -set g_balance_seeker_missile_force 150 // LOG: 100 -> 150 -set g_balance_seeker_missile_health 5 -set g_balance_seeker_missile_lifetime 15 -set g_balance_seeker_missile_proxy 0 -set g_balance_seeker_missile_proxy_delay 0.2 -set g_balance_seeker_missile_proxy_maxrange 45 -set g_balance_seeker_missile_radius 80 -set g_balance_seeker_missile_refire 0.5 -set g_balance_seeker_missile_smart 1 -set g_balance_seeker_missile_smart_mindist 800 -set g_balance_seeker_missile_smart_trace_max 2500 -set g_balance_seeker_missile_smart_trace_min 1000 -set g_balance_seeker_missile_speed 700 -set g_balance_seeker_missile_speed_up 300 -set g_balance_seeker_missile_speed_z 0 -set g_balance_seeker_missile_speed_max 1300 // LOG: 1400 -> 1300 -set g_balance_seeker_missile_spread 0 -set g_balance_seeker_missile_turnrate 0.65 -set g_balance_seeker_tag_ammo 1 -set g_balance_seeker_tag_animtime 0.2 -set g_balance_seeker_tag_damageforcescale 4 -set g_balance_seeker_tag_health 5 -set g_balance_seeker_tag_lifetime 15 -set g_balance_seeker_tag_refire 0.75 // LOG: 0.7 -> 0.75 -set g_balance_seeker_tag_speed 5000 -set g_balance_seeker_tag_spread 0 -set g_balance_seeker_tag_tracker_lifetime 10 -set g_balance_seeker_reload_ammo 0 //default: 15 -set g_balance_seeker_reload_time 2 -// End new seeker diff --git a/balanceXDF.cfg b/balanceXDF.cfg new file mode 100644 index 0000000000..543a5c6176 --- /dev/null +++ b/balanceXDF.cfg @@ -0,0 +1,680 @@ +g_mod_balance XDF + +// {{{ starting gear +set g_start_weapon_laser 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" +set g_start_weapon_shotgun 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" +set g_start_weapon_uzi 1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" +set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" // UNTIL IT CAN BE REMOVED FROM CODE +set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_balance_health_start 100 +set g_balance_armor_start 0 +set g_start_ammo_shells 15 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 100 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 200 +set g_lms_start_armor 200 +set g_lms_start_ammo_shells 60 +set g_lms_start_ammo_nails 320 +set g_lms_start_ammo_rockets 160 +set g_lms_start_ammo_cells 180 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 60 +set g_balance_nix_ammo_nails 320 +set g_balance_nix_ammo_rockets 160 +set g_balance_nix_ammo_cells 180 +set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 1 +set g_pickup_weapons_anyway 1 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 60 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 320 +set g_pickup_rockets 40 +set g_pickup_rockets_weapon 40 +set g_pickup_rockets_max 160 +set g_pickup_cells 30 +set g_pickup_cells_weapon 30 +set g_pickup_cells_max 180 +set g_pickup_fuel 50 +set g_pickup_fuel_weapon 50 +set g_pickup_fuel_jetpack 100 +set g_pickup_fuel_max 100 +set g_pickup_armorsmall 5 +set g_pickup_armorsmall_max 200 +set g_pickup_armorsmall_anyway 1 +set g_pickup_armormedium 25 +set g_pickup_armormedium_max 200 +set g_pickup_armormedium_anyway 1 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 200 +set g_pickup_armorbig_anyway 1 +set g_pickup_armorlarge 100 +set g_pickup_armorlarge_max 200 +set g_pickup_armorlarge_anyway 1 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 200 +set g_pickup_healthsmall_anyway 1 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 200 +set g_pickup_healthmedium_anyway 1 +set g_pickup_healthlarge 50 +set g_pickup_healthlarge_max 200 +set g_pickup_healthlarge_anyway 1 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 200 +set g_pickup_healthmega_anyway 1 +set g_pickup_respawntime_short 0.1 +set g_pickup_respawntime_medium 0.1 +set g_pickup_respawntime_long 0.1 +set g_pickup_respawntime_powerup 0.1 +set g_pickup_respawntime_weapon 0.1 +set g_pickup_respawntime_superweapon 0.1 +set g_pickup_respawntime_ammo 0.1 +set g_pickup_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 30 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.08 +set g_balance_health_regenlinear 0.5 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.04 +set g_balance_health_rotlinear 0.75 +set g_balance_pause_health_rot 1 +set g_balance_pause_health_rot_spawn 5 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.04 +set g_balance_armor_rotlinear 0.75 +set g_balance_pause_armor_rot 1 +set g_balance_pause_armor_rot_spawn 5 +set g_balance_armor_regenstable 100 +set g_balance_armor_rotstable 100 +set g_balance_armor_limit 999 +set g_balance_armor_blockpercent 0.6 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" +set g_balance_fuel_regenlinear 0 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0 +set g_balance_weaponswitchdelay 0 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 0.4 +set g_throughfloor_force 0.7 +set g_projectiles_damage 2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 +set g_projectiles_newton_style 2 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.8 +set g_projectiles_newton_style_2_maxfactor 1.5 +set g_projectiles_spread_style 7 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 250 +set g_balance_falldamage_minspeed 900 +set g_balance_falldamage_factor 0.20 +set g_balance_falldamage_maxdamage 40 +set g_balance_damagepush_speedfactor 2.5 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken +set g_balance_powerup_invincible_time 999 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 3 +set g_balance_powerup_strength_time 999 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 130 +set g_balance_grapplehook_damagedbycontents 0 +// }}} + +// {{{ weapon properties +// {{{ laser +set g_balance_laser_primary_damage 25 +set g_balance_laser_primary_edgedamage 12.5 +set g_balance_laser_primary_force 250 +set g_balance_laser_primary_radius 70 +set g_balance_laser_primary_speed 6000 +set g_balance_laser_primary_spread 0 +set g_balance_laser_primary_refire 0.7 +set g_balance_laser_primary_animtime 0.3 +set g_balance_laser_primary_lifetime 5 +set g_balance_laser_primary_shotangle 0 +set g_balance_laser_primary_delay 0 +set g_balance_laser_primary_gauntlet 0 +set g_balance_laser_primary_force_zscale 1.5 +set g_balance_laser_primary_force_velocitybias 0 +set g_balance_laser_primary_force_other_scale 1 +set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists +set g_balance_laser_secondary_damage 25 +set g_balance_laser_secondary_edgedamage 12.5 +set g_balance_laser_secondary_force 400 +set g_balance_laser_secondary_radius 70 +set g_balance_laser_secondary_speed 12000 +set g_balance_laser_secondary_spread 0 +set g_balance_laser_secondary_refire 0.7 +set g_balance_laser_secondary_animtime 0.3 +set g_balance_laser_secondary_lifetime 5 +set g_balance_laser_secondary_shotangle -90 +set g_balance_laser_secondary_delay 0 +set g_balance_laser_secondary_gauntlet 0 +set g_balance_laser_secondary_force_zscale 1.25 +set g_balance_laser_secondary_force_velocitybias 0 +set g_balance_laser_secondary_force_other_scale 1 +set g_balance_laser_reload_ammo 0 //default: 6 +set g_balance_laser_reload_time 2 +// }}} +// {{{ shotgun +set g_balance_shotgun_primary_bullets 14 +set g_balance_shotgun_primary_damage 4 +set g_balance_shotgun_primary_force 15 +set g_balance_shotgun_primary_spread 0.12 +set g_balance_shotgun_primary_refire 0.75 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_ammo 1 +set g_balance_shotgun_primary_speed 8000 +set g_balance_shotgun_primary_bulletconstant 75 // 3.8qu +set g_balance_shotgun_secondary 1 +set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow +set g_balance_shotgun_secondary_melee_range 120 +set g_balance_shotgun_secondary_melee_swing_side 120 +set g_balance_shotgun_secondary_melee_swing_up 30 +set g_balance_shotgun_secondary_melee_time 0.15 +set g_balance_shotgun_secondary_melee_traces 10 +set g_balance_shotgun_secondary_melee_no_doubleslap 1 +set g_balance_shotgun_secondary_melee_nonplayerdamage 40 +set g_balance_shotgun_secondary_melee_multihit 1 +set g_balance_shotgun_secondary_damage 80 +set g_balance_shotgun_secondary_force 200 +set g_balance_shotgun_secondary_refire 1.25 +set g_balance_shotgun_secondary_animtime 1 +set g_balance_shotgun_reload_ammo 0 //default: 5 +set g_balance_shotgun_reload_time 2 +// }}} +// {{{ uzi +set g_balance_uzi_mode 1 // Activates varible spread for sustained & burst mode secondary +set g_balance_uzi_spread_min 0 +set g_balance_uzi_spread_max 0 +set g_balance_uzi_spread_add 0 + +set g_balance_uzi_burst 3 // # of bullets in a burst (if set to 2 or more) +set g_balance_uzi_burst_animtime 0.3 +set g_balance_uzi_burst_refire 0.06 // refire between burst bullets +set g_balance_uzi_burst_refire2 0.45 // refire after burst +set g_balance_uzi_burst_spread 0.03 +set g_balance_uzi_burst_damage 25 +set g_balance_uzi_burst_force 20 +set g_balance_uzi_burst_ammo 3 + +set g_balance_uzi_first 1 +set g_balance_uzi_first_damage 14 +set g_balance_uzi_first_headshotaddeddamage 0 +set g_balance_uzi_first_force 5 +set g_balance_uzi_first_spread 0.03 +set g_balance_uzi_first_refire 0.4 +set g_balance_uzi_first_ammo 1 + +set g_balance_uzi_sustained_damage 12 +set g_balance_uzi_sustained_headshotaddeddamage 0 +set g_balance_uzi_sustained_force 5 +set g_balance_uzi_sustained_spread 0 +set g_balance_uzi_sustained_refire 0.1 +set g_balance_uzi_sustained_ammo 1 + +set g_balance_uzi_speed 18000 +set g_balance_uzi_bulletconstant 115 // 13.1qu + +set g_balance_uzi_reload_ammo 0 //default: 30 +set g_balance_uzi_reload_time 2 +// }}} +// {{{ mortar +set g_balance_grenadelauncher_primary_type 0 +set g_balance_grenadelauncher_primary_damage 50 +set g_balance_grenadelauncher_primary_edgedamage 25 +set g_balance_grenadelauncher_primary_force 250 +set g_balance_grenadelauncher_primary_radius 100 +set g_balance_grenadelauncher_primary_speed 2000 +set g_balance_grenadelauncher_primary_speed_up 200 +set g_balance_grenadelauncher_primary_speed_z 0 +set g_balance_grenadelauncher_primary_spread 0 +set g_balance_grenadelauncher_primary_lifetime 5 +set g_balance_grenadelauncher_primary_lifetime2 1 +set g_balance_grenadelauncher_primary_refire 0.7 +set g_balance_grenadelauncher_primary_animtime 0.3 +set g_balance_grenadelauncher_primary_ammo 2 +set g_balance_grenadelauncher_primary_health 0 +set g_balance_grenadelauncher_primary_damageforcescale 0 +set g_balance_grenadelauncher_primary_remote_minbouncecnt 0 + +set g_balance_grenadelauncher_secondary_type 1 +set g_balance_grenadelauncher_secondary_damage 60 +set g_balance_grenadelauncher_secondary_edgedamage 30 +set g_balance_grenadelauncher_secondary_force 300 +set g_balance_grenadelauncher_secondary_radius 200 +set g_balance_grenadelauncher_secondary_speed 800 +set g_balance_grenadelauncher_secondary_speed_up 0 +set g_balance_grenadelauncher_secondary_speed_z 200 +set g_balance_grenadelauncher_secondary_spread 0 +set g_balance_grenadelauncher_secondary_lifetime 8 +set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5 +set g_balance_grenadelauncher_secondary_lifetime_stick 0 +set g_balance_grenadelauncher_secondary_refire 0.7 +set g_balance_grenadelauncher_secondary_animtime 0.5 +set g_balance_grenadelauncher_secondary_ammo 2 +set g_balance_grenadelauncher_secondary_health 0 +set g_balance_grenadelauncher_secondary_damageforcescale 0 +set g_balance_grenadelauncher_secondary_remote_detonateprimary 0 + +set g_balance_grenadelauncher_bouncefactor 0.5 +set g_balance_grenadelauncher_bouncestop 0.075 + +set g_balance_grenadelauncher_reload_ammo 0 //default: 12 +set g_balance_grenadelauncher_reload_time 2 +// }}} +// {{{ electro +set g_balance_electro_lightning 0 +set g_balance_electro_primary_damage 55 +set g_balance_electro_primary_edgedamage 27.5 +set g_balance_electro_primary_force 200 +set g_balance_electro_primary_force_up 0 +set g_balance_electro_primary_radius 100 +set g_balance_electro_primary_comboradius 150 +set g_balance_electro_primary_speed 2500 +set g_balance_electro_primary_spread 0 +set g_balance_electro_primary_lifetime 5 +set g_balance_electro_primary_refire 0.6 +set g_balance_electro_primary_animtime 0.1 +set g_balance_electro_primary_ammo 4 +set g_balance_electro_primary_range 0 +set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius +set g_balance_electro_primary_falloff_maxdist 850 +set g_balance_electro_primary_falloff_halflifedist 425 +set g_balance_electro_secondary_damage 40 +set g_balance_electro_secondary_edgedamage 20 +set g_balance_electro_secondary_force 200 +set g_balance_electro_secondary_radius 150 +set g_balance_electro_secondary_speed 900 +set g_balance_electro_secondary_speed_up 200 +set g_balance_electro_secondary_speed_z 0 +set g_balance_electro_secondary_spread 0.05 +set g_balance_electro_secondary_lifetime 3 +set g_balance_electro_secondary_refire 0.2 +set g_balance_electro_secondary_refire2 1.5 +set g_balance_electro_secondary_animtime 0.2 +set g_balance_electro_secondary_ammo 2 +set g_balance_electro_secondary_health 5 +set g_balance_electro_secondary_damageforcescale 4 +set g_balance_electro_secondary_damagedbycontents 1 +set g_balance_electro_secondary_count 3 +set g_balance_electro_secondary_bouncefactor 0.4 +set g_balance_electro_secondary_bouncestop 0.05 +set g_balance_electro_combo_damage 40 +set g_balance_electro_combo_edgedamage 20 +set g_balance_electro_combo_force 120 +set g_balance_electro_combo_radius 175 +set g_balance_electro_combo_comboradius 275 +set g_balance_electro_combo_speed 2000 +set g_balance_electro_combo_safeammocheck 1 +set g_balance_electro_reload_ammo 0 //default: 20 +set g_balance_electro_reload_time 2 +// }}} +// {{{ crylink +set g_balance_crylink_primary_damage 12 +set g_balance_crylink_primary_edgedamage 6 +set g_balance_crylink_primary_force -50 +set g_balance_crylink_primary_radius 80 +set g_balance_crylink_primary_speed 2000 +set g_balance_crylink_primary_spread 0.08 +set g_balance_crylink_primary_shots 6 +set g_balance_crylink_primary_bounces 1 +set g_balance_crylink_primary_refire 0.7 +set g_balance_crylink_primary_animtime 0.3 +set g_balance_crylink_primary_ammo 3 +set g_balance_crylink_primary_bouncedamagefactor 0.5 +set g_balance_crylink_primary_joindelay 0.1 +set g_balance_crylink_primary_joinspread 0.2 +set g_balance_crylink_primary_jointime 0 +set g_balance_crylink_primary_joinexplode 1 +set g_balance_crylink_primary_joinexplode_damage 0 +set g_balance_crylink_primary_joinexplode_edgedamage 0 +set g_balance_crylink_primary_joinexplode_radius 0 +set g_balance_crylink_primary_joinexplode_force 0 +set g_balance_crylink_primary_linkexplode 1 + +set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000 +set g_balance_crylink_primary_middle_fadetime 5 +set g_balance_crylink_primary_other_lifetime 5 +set g_balance_crylink_primary_other_fadetime 5 + +set g_balance_crylink_secondary 1 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -150 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_refire 0.7 +set g_balance_crylink_secondary_animtime 0.2 +set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_bouncedamagefactor 0.5 +set g_balance_crylink_secondary_joindelay 0 +set g_balance_crylink_secondary_joinspread 0 +set g_balance_crylink_secondary_jointime 0 +set g_balance_crylink_secondary_joinexplode 0 +set g_balance_crylink_secondary_joinexplode_damage 0 +set g_balance_crylink_secondary_joinexplode_edgedamage 0 +set g_balance_crylink_secondary_joinexplode_radius 0 +set g_balance_crylink_secondary_joinexplode_force 0 +set g_balance_crylink_secondary_linkexplode 1 + +set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000 +set g_balance_crylink_secondary_middle_fadetime 5 +set g_balance_crylink_secondary_line_lifetime 5 +set g_balance_crylink_secondary_line_fadetime 5 + +set g_balance_crylink_reload_ammo 0 //default: 10 +set g_balance_crylink_reload_time 2 +// }}} +// {{{ nex +set g_balance_nex_primary_damage 90 +set g_balance_nex_primary_force 400 +set g_balance_nex_primary_refire 1.5 +set g_balance_nex_primary_animtime 0.4 +set g_balance_nex_primary_ammo 6 +set g_balance_nex_primary_damagefalloff_mindist 0 // 1000 For tZork ;3 +set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000 +set g_balance_nex_primary_damagefalloff_halflife 0 // 1500 +set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500 + +set g_balance_nex_secondary 0 +set g_balance_nex_secondary_charge 0 +set g_balance_nex_secondary_charge_rate 0.1 +set g_balance_nex_secondary_chargepool 0 +set g_balance_nex_secondary_chargepool_regen 0.15 +set g_balance_nex_secondary_chargepool_pause_regen 1 +set g_balance_nex_secondary_chargepool_pause_health_regen 1 +set g_balance_nex_secondary_damage 0 +set g_balance_nex_secondary_force 0 +set g_balance_nex_secondary_refire 0 +set g_balance_nex_secondary_animtime 0 +set g_balance_nex_secondary_ammo 2 +set g_balance_nex_secondary_damagefalloff_mindist 0 +set g_balance_nex_secondary_damagefalloff_maxdist 0 +set g_balance_nex_secondary_damagefalloff_halflife 0 +set g_balance_nex_secondary_damagefalloff_forcehalflife 0 + +set g_balance_nex_charge 1 +set g_balance_nex_charge_mindmg 40 +set g_balance_nex_charge_start 0.5 +set g_balance_nex_charge_rate 0.4 +set g_balance_nex_charge_animlimit 0.5 +set g_balance_nex_charge_limit 1 +set g_balance_nex_charge_rot_rate 0 +set g_balance_nex_charge_rot_pause 0 // Dont rot down untill this long after release of charge button +set g_balance_nex_charge_shot_multiplier 0 +set g_balance_nex_charge_velocity_rate 0 +set g_balance_nex_charge_minspeed 400 +set g_balance_nex_charge_maxspeed 800 + +set g_balance_nex_reload_ammo 0 //default: 25 +set g_balance_nex_reload_time 2 +// }}} +// {{{ minstanex +set g_balance_minstanex_refire 1 +set g_balance_minstanex_animtime 0.3 +set g_balance_minstanex_ammo 10 +set g_balance_minstanex_laser_ammo 0 +set g_balance_minstanex_laser_animtime 0.3 +set g_balance_minstanex_laser_refire 0.7 +set g_balance_minstanex_reload_ammo 0 //default: 50 +set g_balance_minstanex_reload_time 2 +// }}} +// {{{ hagar +set g_balance_hagar_primary_damage 25 +set g_balance_hagar_primary_edgedamage 12.5 +set g_balance_hagar_primary_force 92 +set g_balance_hagar_primary_health 15 +set g_balance_hagar_primary_damageforcescale 0 +set g_balance_hagar_primary_radius 25 +set g_balance_hagar_primary_spread 0.03 +set g_balance_hagar_primary_speed 2000 +set g_balance_hagar_primary_lifetime 5 +set g_balance_hagar_primary_refire 0.11 +set g_balance_hagar_primary_ammo 1 +set g_balance_hagar_secondary 0 +set g_balance_hagar_secondary_load 1 +set g_balance_hagar_secondary_load_speed 0.5 +set g_balance_hagar_secondary_load_spread 0.075 +set g_balance_hagar_secondary_load_spread_bias 0.5 +set g_balance_hagar_secondary_load_max 4 +set g_balance_hagar_secondary_load_hold 4 +set g_balance_hagar_secondary_load_releasedeath 0 +set g_balance_hagar_secondary_load_abort 0 +set g_balance_hagar_secondary_load_linkexplode 0 +set g_balance_hagar_secondary_load_animtime 0.2 +set g_balance_hagar_secondary_damage 40 +set g_balance_hagar_secondary_edgedamage 20 +set g_balance_hagar_secondary_force 75 +set g_balance_hagar_secondary_health 15 +set g_balance_hagar_secondary_damageforcescale 0 +set g_balance_hagar_secondary_radius 80 +set g_balance_hagar_secondary_spread 0.05 +set g_balance_hagar_secondary_speed 2000 +set g_balance_hagar_secondary_lifetime_min 10 +set g_balance_hagar_secondary_lifetime_rand 0 +set g_balance_hagar_secondary_refire 0.5 +set g_balance_hagar_secondary_ammo 1 +set g_balance_hagar_reload_ammo 0 //default: 25 +set g_balance_hagar_reload_time 2 +// }}} +// {{{ rocketlauncher +set g_balance_rocketlauncher_damage 80 +set g_balance_rocketlauncher_edgedamage 40 +set g_balance_rocketlauncher_force 350 +set g_balance_rocketlauncher_radius 110 +set g_balance_rocketlauncher_speed 1000 +set g_balance_rocketlauncher_speedaccel 0 +set g_balance_rocketlauncher_speedstart 1000 +set g_balance_rocketlauncher_lifetime 100 +set g_balance_rocketlauncher_refire 0.9 +set g_balance_rocketlauncher_animtime 0.7 +set g_balance_rocketlauncher_ammo 4 +set g_balance_rocketlauncher_health 0 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time. +set g_balance_rocketlauncher_damageforcescale 0 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn +set g_balance_rocketlauncher_detonatedelay 999 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time +set g_balance_rocketlauncher_guiderate 0 // max degrees per second +set g_balance_rocketlauncher_guideratedelay 0.01 // immediate +set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic) +set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in +set g_balance_rocketlauncher_guidestop 1 // stop guiding when firing again +set g_balance_rocketlauncher_remote_damage 70 +set g_balance_rocketlauncher_remote_edgedamage 35 +set g_balance_rocketlauncher_remote_radius 110 +set g_balance_rocketlauncher_remote_force 350 +set g_balance_rocketlauncher_reload_ammo 0 //default: 25 +set g_balance_rocketlauncher_reload_time 2 +// }}} +// {{{ porto +set g_balance_porto_primary_refire 1.5 +set g_balance_porto_primary_animtime 0.3 +set g_balance_porto_primary_speed 5000 +set g_balance_porto_primary_lifetime 5 +set g_balance_porto_secondary 1 +set g_balance_porto_secondary_refire 1.5 +set g_balance_porto_secondary_animtime 0.3 +set g_balance_porto_secondary_speed 1000 +set g_balance_porto_secondary_lifetime 5 +set g_balance_portal_health 200 // these get recharged whenever the portal is used +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used +// }}} +// {{{ hook +set g_balance_hook_primary_fuel 5 // hook monkeys set 0 +set g_balance_hook_primary_refire 0 // hook monkeys set 0 +set g_balance_hook_primary_animtime 0.3 // good shoot anim +set g_balance_hook_primary_hooked_time_max 0 // infinite +set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free +set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked +set g_balance_hook_secondary_damage 25 // not much +set g_balance_hook_secondary_edgedamage 5 // not much +set g_balance_hook_secondary_radius 500 // LOTS +set g_balance_hook_secondary_force -2000 // LOTS +set g_balance_hook_secondary_ammo 50 // a whole pack +set g_balance_hook_secondary_lifetime 5 // infinite +set g_balance_hook_secondary_speed 0 // not much throwing +set g_balance_hook_secondary_gravity 5 // fast falling +set g_balance_hook_secondary_refire 3 // don't drop too many bombs... +set g_balance_hook_secondary_animtime 0.3 // good shoot anim +set g_balance_hook_secondary_power 3 // effect behaves like a square function +set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds +set g_balance_hook_secondary_health 15 +set g_balance_hook_secondary_damageforcescale 0 +// }}} +// {{{ tuba +set g_balance_tuba_refire 0.05 +set g_balance_tuba_animtime 0.05 +set g_balance_tuba_attenuation 0.5 +set g_balance_tuba_volume 1 +set g_balance_tuba_fadetime 0.25 +set g_balance_tuba_damage 5 +set g_balance_tuba_edgedamage 0 +set g_balance_tuba_radius 200 +set g_balance_tuba_force 40 +set g_balance_tuba_pitchstep 6 +// }}} +// {{{ fireball // this is a superweapon -- lets make it behave as one. +set g_balance_fireball_primary_animtime 0.2 +set g_balance_fireball_primary_bfgdamage 100 +set g_balance_fireball_primary_bfgforce 0 +set g_balance_fireball_primary_bfgradius 1000 +set g_balance_fireball_primary_damage 200 +set g_balance_fireball_primary_damageforcescale 0 +set g_balance_fireball_primary_edgedamage 50 +set g_balance_fireball_primary_force 600 +set g_balance_fireball_primary_health 0 +set g_balance_fireball_primary_laserburntime 0.5 +set g_balance_fireball_primary_laserdamage 80 +set g_balance_fireball_primary_laseredgedamage 20 +set g_balance_fireball_primary_laserradius 256 +set g_balance_fireball_primary_lifetime 15 +set g_balance_fireball_primary_radius 200 +set g_balance_fireball_primary_refire 2 +set g_balance_fireball_primary_refire2 0 +set g_balance_fireball_primary_speed 1200 +set g_balance_fireball_primary_spread 0 +set g_balance_fireball_secondary_animtime 0.3 +set g_balance_fireball_secondary_damage 40 +set g_balance_fireball_secondary_damageforcescale 4 +set g_balance_fireball_secondary_damagetime 5 +set g_balance_fireball_secondary_force 100 +set g_balance_fireball_secondary_laserburntime 0.5 +set g_balance_fireball_secondary_laserdamage 50 +set g_balance_fireball_secondary_laseredgedamage 20 +set g_balance_fireball_secondary_laserradius 110 +set g_balance_fireball_secondary_lifetime 7 +set g_balance_fireball_secondary_refire 1.5 +set g_balance_fireball_secondary_speed 900 +set g_balance_fireball_secondary_speed_up 100 +set g_balance_fireball_secondary_speed_z 0 +set g_balance_fireball_secondary_spread 0 +// }}} diff --git a/balanceXPM.cfg b/balanceXPM.cfg index 2da131c96c..cd440294c0 100644 --- a/balanceXPM.cfg +++ b/balanceXPM.cfg @@ -5,7 +5,6 @@ set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide th set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minelayer -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" @@ -14,11 +13,8 @@ set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always p set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hlac -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_rifle -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_seeker -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" set g_balance_health_start 100 set g_balance_armor_start 0 set g_start_ammo_shells 15 @@ -162,17 +158,16 @@ set g_throughfloor_force 0.75 set g_projectiles_damage 1 // possible values: // -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, minelayer mines) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) // 0: only damage from contents (lava/slime) or exceptions // 1: only self damage or damage from contents or exceptions // 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 set g_projectiles_newton_style 0 // possible values: // 0: absolute velocity projectiles (like Quake) // 1: relative velocity projectiles, "Newtonian" (like Tribes 2) // 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) -// 3: absolute velocity + player velocity component in shot direction (note: does NOT yield the right relative velocity, but may be good enough, but it is somewhat prone to sniper rockets) -// 4: just add the player velocity length to the absolute velocity (tZork's sniper rockets) set g_projectiles_newton_style_2_minfactor 0.8 set g_projectiles_newton_style_2_maxfactor 1.5 set g_projectiles_spread_style 7 @@ -374,31 +369,6 @@ set g_balance_grenadelauncher_bouncestop 0.075 set g_balance_grenadelauncher_reload_ammo 0 //default: 12 set g_balance_grenadelauncher_reload_time 2 // }}} -// {{{ minelayer -set g_balance_minelayer_damage 40 -set g_balance_minelayer_edgedamage 20 -set g_balance_minelayer_force 250 -set g_balance_minelayer_radius 175 -set g_balance_minelayer_proximityradius 150 -set g_balance_minelayer_speed 1000 -set g_balance_minelayer_lifetime 10 -set g_balance_minelayer_lifetime_countdown 0.5 -set g_balance_minelayer_refire 1.5 -set g_balance_minelayer_animtime 0.4 -set g_balance_minelayer_ammo 4 -set g_balance_minelayer_health 15 -set g_balance_minelayer_limit 3 // 0 disables the limit -set g_balance_minelayer_protection 0 // don't explode if the mine would hurt the owner or a team mate -set g_balance_minelayer_damageforcescale 0 -set g_balance_minelayer_detonatedelay -1 // 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_minelayer_time 0.5 -set g_balance_minelayer_remote_damage 45 -set g_balance_minelayer_remote_edgedamage 40 -set g_balance_minelayer_remote_radius 200 -set g_balance_minelayer_remote_force 300 -set g_balance_minelayer_reload_ammo 0 //default: 15 -set g_balance_minelayer_reload_time 2 -// }}} // {{{ electro set g_balance_electro_lightning 0 set g_balance_electro_primary_damage 40 @@ -425,9 +395,9 @@ set g_balance_electro_secondary_speed 1000 set g_balance_electro_secondary_speed_up 200 set g_balance_electro_secondary_speed_z 0 set g_balance_electro_secondary_spread 0.04 -set g_balance_electro_secondary_lifetime 3 +set g_balance_electro_secondary_lifetime 4 set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 1.5 +set g_balance_electro_secondary_refire2 1.6 set g_balance_electro_secondary_animtime 0.2 set g_balance_electro_secondary_ammo 2 set g_balance_electro_secondary_health 5 @@ -439,7 +409,7 @@ set g_balance_electro_secondary_bouncestop 0.05 set g_balance_electro_combo_damage 50 set g_balance_electro_combo_edgedamage 25 set g_balance_electro_combo_force 120 -set g_balance_electro_combo_radius 175 +set g_balance_electro_combo_radius 150 set g_balance_electro_combo_comboradius 300 set g_balance_electro_combo_speed 2000 set g_balance_electro_combo_safeammocheck 1 @@ -449,7 +419,7 @@ set g_balance_electro_reload_time 2 // {{{ crylink set g_balance_crylink_primary_damage 12 set g_balance_crylink_primary_edgedamage 6 -set g_balance_crylink_primary_force -60 +set g_balance_crylink_primary_force -50 set g_balance_crylink_primary_radius 80 set g_balance_crylink_primary_speed 2000 set g_balance_crylink_primary_spread 0.08 @@ -475,15 +445,16 @@ set g_balance_crylink_primary_other_lifetime 5 set g_balance_crylink_primary_other_fadetime 5 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 5 -set g_balance_crylink_secondary_edgedamage 0 -set g_balance_crylink_secondary_force -40 -set g_balance_crylink_secondary_radius 70 -set g_balance_crylink_secondary_speed 2000 -set g_balance_crylink_secondary_spread 0.02 -set g_balance_crylink_secondary_shots 3 -set g_balance_crylink_secondary_bounces 1 -set g_balance_crylink_secondary_refire 0.2 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -150 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_refire 0.7 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_ammo 2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 @@ -659,77 +630,6 @@ 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 // }}} -// {{{ hlac // TO BE REMOVED -set g_balance_hlac_primary_spread_min 0.01 -set g_balance_hlac_primary_spread_max 0.25 -set g_balance_hlac_primary_spread_add 0.0045 -set g_balance_hlac_primary_spread_crouchmod 0.25 - -set g_balance_hlac_primary_damage 18 -set g_balance_hlac_primary_edgedamage 9 -set g_balance_hlac_primary_force 90 -set g_balance_hlac_primary_radius 70 -set g_balance_hlac_primary_speed 9000 -set g_balance_hlac_primary_lifetime 5 - -set g_balance_hlac_primary_refire 0.15 -set g_balance_hlac_primary_animtime 0.4 -set g_balance_hlac_primary_ammo 1 - -set g_balance_hlac_secondary 1 -set g_balance_hlac_secondary_spread 0.15 -set g_balance_hlac_secondary_spread_crouchmod 0.5 - -set g_balance_hlac_secondary_damage 15 -set g_balance_hlac_secondary_edgedamage 7.5 -set g_balance_hlac_secondary_force 90 -set g_balance_hlac_secondary_radius 70 -set g_balance_hlac_secondary_speed 9000 -set g_balance_hlac_secondary_lifetime 5 - -set g_balance_hlac_secondary_refire 1 -set g_balance_hlac_secondary_animtime 0.3 -set g_balance_hlac_secondary_ammo 10 -set g_balance_hlac_secondary_shots 6 - -set g_balance_hlac_reload_ammo 0 //default: 20 -set g_balance_hlac_reload_time 2 -// }}} -// {{{ rifle -set g_balance_rifle_bursttime 0 -set g_balance_rifle_primary_tracer 1 -set g_balance_rifle_primary_shots 1 -set g_balance_rifle_primary_damage 40 -set g_balance_rifle_primary_headshotaddeddamage 40 -set g_balance_rifle_primary_spread 0 -set g_balance_rifle_primary_force 100 -set g_balance_rifle_primary_speed 40000 -set g_balance_rifle_primary_lifetime 5 -set g_balance_rifle_primary_refire 1.2 -set g_balance_rifle_primary_animtime 0.4 -set g_balance_rifle_primary_ammo 10 -set g_balance_rifle_primary_bulletconstant 110 // 62.2qu -set g_balance_rifle_primary_burstcost 0 -set g_balance_rifle_primary_bullethail 0 // empty magazine on shot -set g_balance_rifle_secondary 1 -set g_balance_rifle_secondary_reload 0 -set g_balance_rifle_secondary_tracer 0 -set g_balance_rifle_secondary_shots 4 -set g_balance_rifle_secondary_damage 10 -set g_balance_rifle_secondary_headshotaddeddamage 20 -set g_balance_rifle_secondary_spread 0.04 -set g_balance_rifle_secondary_force 50 -set g_balance_rifle_secondary_speed 20000 -set g_balance_rifle_secondary_lifetime 5 -set g_balance_rifle_secondary_refire 0.9 -set g_balance_rifle_secondary_animtime 0.3 -set g_balance_rifle_secondary_ammo 10 -set g_balance_rifle_secondary_bulletconstant 110 // 15.5qu -set g_balance_rifle_secondary_burstcost 0 -set g_balance_rifle_secondary_bullethail 0 // empty magazine on shot -set g_balance_rifle_reload_ammo 80 //default: 80 -set g_balance_rifle_reload_time 2 -// }}} // {{{ tuba set g_balance_tuba_refire 0.05 set g_balance_tuba_animtime 0.05 @@ -778,57 +678,3 @@ set g_balance_fireball_secondary_speed_up 100 set g_balance_fireball_secondary_speed_z 0 set g_balance_fireball_secondary_spread 0 // }}} -// {{{ seeker -set g_balance_seeker_type 1 // 0 = old seeker, 1 = new seeker. THIS IS A TEMPORARY CVAR FOR TESTING, will be removed later. -set g_balance_seeker_flac_ammo 0.5 -set g_balance_seeker_flac_animtime 0.1 -set g_balance_seeker_flac_damage 15 -set g_balance_seeker_flac_edgedamage 10 -set g_balance_seeker_flac_force 50 -set g_balance_seeker_flac_lifetime 0.1 -set g_balance_seeker_flac_lifetime_rand 0.05 -set g_balance_seeker_flac_radius 100 -set g_balance_seeker_flac_refire 0.1 -set g_balance_seeker_flac_speed 3000 -set g_balance_seeker_flac_speed_up 1000 -set g_balance_seeker_flac_speed_z 0 -set g_balance_seeker_flac_spread 0.4 -set g_balance_seeker_tag_ammo 1 -set g_balance_seeker_tag_animtime 0.2 -set g_balance_seeker_tag_damageforcescale 0 -set g_balance_seeker_tag_health 0 -set g_balance_seeker_tag_lifetime 15 -set g_balance_seeker_tag_refire 0.75 // LOG: 0.7 -> 0.75 -set g_balance_seeker_tag_speed 5000 -set g_balance_seeker_tag_spread 0 -set g_balance_seeker_tag_tracker_lifetime 10 -set g_balance_seeker_missile_accel 1500 -set g_balance_seeker_missile_ammo 2 -set g_balance_seeker_missile_animtime 0.2 -set g_balance_seeker_missile_count 3 // LOG: 8 -> 3 -set g_balance_seeker_missile_damage 16 // LOG: 15 -> 30 -set g_balance_seeker_missile_damageforcescale 4 -set g_balance_seeker_missile_decel 6000 -set g_balance_seeker_missile_delay 0.25 -set g_balance_seeker_missile_edgedamage 8 -set g_balance_seeker_missile_force 50 // LOG: 100 -> 150 -set g_balance_seeker_missile_health 1 -set g_balance_seeker_missile_lifetime 15 -set g_balance_seeker_missile_proxy 0 -set g_balance_seeker_missile_proxy_delay 0.2 -set g_balance_seeker_missile_proxy_maxrange 45 -set g_balance_seeker_missile_radius 70 -set g_balance_seeker_missile_refire 0.25 -set g_balance_seeker_missile_smart 0 -set g_balance_seeker_missile_smart_mindist 800 -set g_balance_seeker_missile_smart_trace_max 2500 -set g_balance_seeker_missile_smart_trace_min 1000 -set g_balance_seeker_missile_speed 1500 -set g_balance_seeker_missile_speed_up 0 -set g_balance_seeker_missile_speed_z 0 -set g_balance_seeker_missile_speed_max 2000 // LOG: 1400 -> 1300 -set g_balance_seeker_missile_spread 0 -set g_balance_seeker_missile_turnrate 0.15 -set g_balance_seeker_reload_ammo 0 //default: 15 -set g_balance_seeker_reload_time 2 -// End new seeker diff --git a/balanceXonotic.cfg b/balanceXonotic.cfg index 2a4c36989d..871b9a390f 100644 --- a/balanceXonotic.cfg +++ b/balanceXonotic.cfg @@ -1,24 +1,20 @@ g_mod_balance Xonotic // {{{ starting gear -set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minelayer -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hlac -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_rifle -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_seeker -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_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" +set g_start_weapon_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 @@ -162,17 +158,16 @@ set g_throughfloor_force 0.75 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, minelayer mines) +// -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) -// 3: absolute velocity + player velocity component in shot direction (note: does NOT yield the right relative velocity, but may be good enough, but it is somewhat prone to sniper rockets) -// 4: just add the player velocity length to the absolute velocity (tZork's sniper rockets) set g_projectiles_newton_style_2_minfactor 0.8 set g_projectiles_newton_style_2_maxfactor 1.5 set g_projectiles_spread_style 7 @@ -374,31 +369,6 @@ set g_balance_grenadelauncher_bouncestop 0.075 set g_balance_grenadelauncher_reload_ammo 0 //default: 12 set g_balance_grenadelauncher_reload_time 2 // }}} -// {{{ minelayer -set g_balance_minelayer_damage 40 -set g_balance_minelayer_edgedamage 20 -set g_balance_minelayer_force 250 -set g_balance_minelayer_radius 175 -set g_balance_minelayer_proximityradius 150 -set g_balance_minelayer_speed 1000 -set g_balance_minelayer_lifetime 10 -set g_balance_minelayer_lifetime_countdown 0.5 -set g_balance_minelayer_refire 1.5 -set g_balance_minelayer_animtime 0.4 -set g_balance_minelayer_ammo 4 -set g_balance_minelayer_health 15 -set g_balance_minelayer_limit 3 // 0 disables the limit -set g_balance_minelayer_protection 0 // don't explode if the mine would hurt the owner or a team mate -set g_balance_minelayer_damageforcescale 0 -set g_balance_minelayer_detonatedelay -1 // 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_minelayer_time 0.5 -set g_balance_minelayer_remote_damage 45 -set g_balance_minelayer_remote_edgedamage 40 -set g_balance_minelayer_remote_radius 200 -set g_balance_minelayer_remote_force 300 -set g_balance_minelayer_reload_ammo 0 //default: 15 -set g_balance_minelayer_reload_time 2 -// }}} // {{{ electro set g_balance_electro_lightning 0 set g_balance_electro_primary_damage 40 @@ -425,9 +395,9 @@ set g_balance_electro_secondary_speed 1000 set g_balance_electro_secondary_speed_up 200 set g_balance_electro_secondary_speed_z 0 set g_balance_electro_secondary_spread 0.04 -set g_balance_electro_secondary_lifetime 3 +set g_balance_electro_secondary_lifetime 4 set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 1.5 +set g_balance_electro_secondary_refire2 1.6 set g_balance_electro_secondary_animtime 0.2 set g_balance_electro_secondary_ammo 2 set g_balance_electro_secondary_health 5 @@ -439,7 +409,7 @@ set g_balance_electro_secondary_bouncestop 0.05 set g_balance_electro_combo_damage 50 set g_balance_electro_combo_edgedamage 25 set g_balance_electro_combo_force 120 -set g_balance_electro_combo_radius 175 +set g_balance_electro_combo_radius 150 set g_balance_electro_combo_comboradius 300 set g_balance_electro_combo_speed 2000 set g_balance_electro_combo_safeammocheck 1 @@ -449,7 +419,7 @@ set g_balance_electro_reload_time 2 // {{{ crylink set g_balance_crylink_primary_damage 12 set g_balance_crylink_primary_edgedamage 6 -set g_balance_crylink_primary_force -60 +set g_balance_crylink_primary_force -50 set g_balance_crylink_primary_radius 80 set g_balance_crylink_primary_speed 2000 set g_balance_crylink_primary_spread 0.08 @@ -475,15 +445,16 @@ set g_balance_crylink_primary_other_lifetime 5 set g_balance_crylink_primary_other_fadetime 5 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 5 -set g_balance_crylink_secondary_edgedamage 0 -set g_balance_crylink_secondary_force -40 -set g_balance_crylink_secondary_radius 70 -set g_balance_crylink_secondary_speed 2000 -set g_balance_crylink_secondary_spread 0.02 -set g_balance_crylink_secondary_shots 3 -set g_balance_crylink_secondary_bounces 1 -set g_balance_crylink_secondary_refire 0.2 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -150 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_refire 0.7 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_ammo 2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 @@ -659,77 +630,6 @@ 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 // }}} -// {{{ hlac // TO BE REMOVED -set g_balance_hlac_primary_spread_min 0.01 -set g_balance_hlac_primary_spread_max 0.25 -set g_balance_hlac_primary_spread_add 0.0045 -set g_balance_hlac_primary_spread_crouchmod 0.25 - -set g_balance_hlac_primary_damage 18 -set g_balance_hlac_primary_edgedamage 9 -set g_balance_hlac_primary_force 90 -set g_balance_hlac_primary_radius 70 -set g_balance_hlac_primary_speed 9000 -set g_balance_hlac_primary_lifetime 5 - -set g_balance_hlac_primary_refire 0.15 -set g_balance_hlac_primary_animtime 0.4 -set g_balance_hlac_primary_ammo 1 - -set g_balance_hlac_secondary 1 -set g_balance_hlac_secondary_spread 0.15 -set g_balance_hlac_secondary_spread_crouchmod 0.5 - -set g_balance_hlac_secondary_damage 15 -set g_balance_hlac_secondary_edgedamage 7.5 -set g_balance_hlac_secondary_force 90 -set g_balance_hlac_secondary_radius 70 -set g_balance_hlac_secondary_speed 9000 -set g_balance_hlac_secondary_lifetime 5 - -set g_balance_hlac_secondary_refire 1 -set g_balance_hlac_secondary_animtime 0.3 -set g_balance_hlac_secondary_ammo 10 -set g_balance_hlac_secondary_shots 6 - -set g_balance_hlac_reload_ammo 0 //default: 20 -set g_balance_hlac_reload_time 2 -// }}} -// {{{ rifle -set g_balance_rifle_bursttime 0 -set g_balance_rifle_primary_tracer 1 -set g_balance_rifle_primary_shots 1 -set g_balance_rifle_primary_damage 40 -set g_balance_rifle_primary_headshotaddeddamage 40 -set g_balance_rifle_primary_spread 0 -set g_balance_rifle_primary_force 100 -set g_balance_rifle_primary_speed 40000 -set g_balance_rifle_primary_lifetime 5 -set g_balance_rifle_primary_refire 1.2 -set g_balance_rifle_primary_animtime 0.4 -set g_balance_rifle_primary_ammo 10 -set g_balance_rifle_primary_bulletconstant 110 // 62.2qu -set g_balance_rifle_primary_burstcost 0 -set g_balance_rifle_primary_bullethail 0 // empty magazine on shot -set g_balance_rifle_secondary 1 -set g_balance_rifle_secondary_reload 0 -set g_balance_rifle_secondary_tracer 0 -set g_balance_rifle_secondary_shots 4 -set g_balance_rifle_secondary_damage 10 -set g_balance_rifle_secondary_headshotaddeddamage 20 -set g_balance_rifle_secondary_spread 0.04 -set g_balance_rifle_secondary_force 50 -set g_balance_rifle_secondary_speed 20000 -set g_balance_rifle_secondary_lifetime 5 -set g_balance_rifle_secondary_refire 0.9 -set g_balance_rifle_secondary_animtime 0.3 -set g_balance_rifle_secondary_ammo 10 -set g_balance_rifle_secondary_bulletconstant 110 // 15.5qu -set g_balance_rifle_secondary_burstcost 0 -set g_balance_rifle_secondary_bullethail 0 // empty magazine on shot -set g_balance_rifle_reload_ammo 80 //default: 80 -set g_balance_rifle_reload_time 2 -// }}} // {{{ tuba set g_balance_tuba_refire 0.05 set g_balance_tuba_animtime 0.05 @@ -778,57 +678,3 @@ set g_balance_fireball_secondary_speed_up 100 set g_balance_fireball_secondary_speed_z 0 set g_balance_fireball_secondary_spread 0 // }}} -// {{{ seeker -set g_balance_seeker_type 1 // 0 = old seeker, 1 = new seeker. THIS IS A TEMPORARY CVAR FOR TESTING, will be removed later. -set g_balance_seeker_flac_ammo 0.5 -set g_balance_seeker_flac_animtime 0.1 -set g_balance_seeker_flac_damage 15 -set g_balance_seeker_flac_edgedamage 10 -set g_balance_seeker_flac_force 50 -set g_balance_seeker_flac_lifetime 0.1 -set g_balance_seeker_flac_lifetime_rand 0.05 -set g_balance_seeker_flac_radius 100 -set g_balance_seeker_flac_refire 0.1 -set g_balance_seeker_flac_speed 3000 -set g_balance_seeker_flac_speed_up 1000 -set g_balance_seeker_flac_speed_z 0 -set g_balance_seeker_flac_spread 0.4 -set g_balance_seeker_tag_ammo 1 -set g_balance_seeker_tag_animtime 0.2 -set g_balance_seeker_tag_damageforcescale 0 -set g_balance_seeker_tag_health 0 -set g_balance_seeker_tag_lifetime 15 -set g_balance_seeker_tag_refire 0.75 // LOG: 0.7 -> 0.75 -set g_balance_seeker_tag_speed 5000 -set g_balance_seeker_tag_spread 0 -set g_balance_seeker_tag_tracker_lifetime 10 -set g_balance_seeker_missile_accel 1500 -set g_balance_seeker_missile_ammo 2 -set g_balance_seeker_missile_animtime 0.2 -set g_balance_seeker_missile_count 3 // LOG: 8 -> 3 -set g_balance_seeker_missile_damage 16 // LOG: 15 -> 30 -set g_balance_seeker_missile_damageforcescale 4 -set g_balance_seeker_missile_decel 6000 -set g_balance_seeker_missile_delay 0.25 -set g_balance_seeker_missile_edgedamage 8 -set g_balance_seeker_missile_force 50 // LOG: 100 -> 150 -set g_balance_seeker_missile_health 1 -set g_balance_seeker_missile_lifetime 15 -set g_balance_seeker_missile_proxy 0 -set g_balance_seeker_missile_proxy_delay 0.2 -set g_balance_seeker_missile_proxy_maxrange 45 -set g_balance_seeker_missile_radius 70 -set g_balance_seeker_missile_refire 0.25 -set g_balance_seeker_missile_smart 0 -set g_balance_seeker_missile_smart_mindist 800 -set g_balance_seeker_missile_smart_trace_max 2500 -set g_balance_seeker_missile_smart_trace_min 1000 -set g_balance_seeker_missile_speed 1500 -set g_balance_seeker_missile_speed_up 0 -set g_balance_seeker_missile_speed_z 0 -set g_balance_seeker_missile_speed_max 2000 // LOG: 1400 -> 1300 -set g_balance_seeker_missile_spread 0 -set g_balance_seeker_missile_turnrate 0.15 -set g_balance_seeker_reload_ammo 0 //default: 15 -set g_balance_seeker_reload_time 2 -// End new seeker diff --git a/build-compat-pack.sh b/build-compat-pack.sh deleted file mode 100755 index e3772c51b5..0000000000 --- a/build-compat-pack.sh +++ /dev/null @@ -1,460 +0,0 @@ -#!/bin/sh - -# list of files v2.4.2 clients need to play on svn servers - -COMPAT_FILES=" - effectinfo.txt - gfx/crosshairtuba.tga - gfx/hud/inv_weapon0.tga - gfx/hud/inv_weapon10.tga - gfx/hud/inv_weapon11.tga - gfx/hud/inv_weapon12.tga - gfx/hud/inv_weapon13.tga - gfx/hud/inv_weapon14.tga - gfx/hud/inv_weapon1.tga - gfx/hud/inv_weapon2.tga - gfx/hud/inv_weapon3.tga - gfx/hud/inv_weapon4.tga - gfx/hud/inv_weapon5.tga - gfx/hud/inv_weapon6.tga - gfx/hud/inv_weapon7.tga - gfx/hud/inv_weapon8.tga - gfx/hud/inv_weapon9.tga - gfx/hud/inv_weapon_hlacmod_renameit.tga - gfx/hud/keys/key_backward_inv.tga - gfx/hud/keys/key_backward.tga - gfx/hud/keys/key_bg.tga - gfx/hud/keys/key_crouch_inv.tga - gfx/hud/keys/key_crouch.tga - gfx/hud/keys/key_forward_inv.tga - gfx/hud/keys/key_forward.tga - gfx/hud/keys/key_jump_inv.tga - gfx/hud/keys/key_jump.tga - gfx/hud/keys/key_left_inv.tga - gfx/hud/keys/key_left.tga - gfx/hud/keys/key_right_inv.tga - gfx/hud/keys/key_right.tga - gfx/hud/num_0_stroke.tga - gfx/hud/num_0.tga - gfx/hud/num_1_stroke.tga - gfx/hud/num_1.tga - gfx/hud/num_2_stroke.tga - gfx/hud/num_2.tga - gfx/hud/num_3_stroke.tga - gfx/hud/num_3.tga - gfx/hud/num_4_stroke.tga - gfx/hud/num_4.tga - gfx/hud/num_5_stroke.tga - gfx/hud/num_5.tga - gfx/hud/num_6_stroke.tga - gfx/hud/num_6.tga - gfx/hud/num_7_stroke.tga - gfx/hud/num_7.tga - gfx/hud/num_8_stroke.tga - gfx/hud/num_8.tga - gfx/hud/num_9_stroke.tga - gfx/hud/num_9.tga - gfx/hud/num_colon_stroke.tga - gfx/hud/num_colon.tga - gfx/hud/num_dot_stroke.tga - gfx/hud/num_dot.tga - gfx/hud/num_minus_stroke.tga - gfx/hud/num_minus.tga - gfx/hud/num_plus_stroke.tga - gfx/hud/num_plus.tga - gfx/hud/rifle_ring_1.tga - gfx/hud/rifle_ring_2.tga - gfx/hud/rifle_ring_3.tga - gfx/hud/rifle_ring_4.tga - gfx/hud/rifle_ring_5.tga - gfx/hud/rifle_ring_6.tga - gfx/hud/rifle_ring_7.tga - gfx/hud/rifle_ring_8.tga - gfx/hud/sb_accuracy.tga - gfx/hud/sb_ammobg.tga - gfx/hud/sb_armor.tga - gfx/hud/sbar.tga - gfx/hud/sb_bullets.tga - gfx/hud/sb_cells.tga - gfx/hud/sb_flag_blue_carrying.tga - gfx/hud/sb_flag_blue_lost.tga - gfx/hud/sb_flag_blue_shielded.tga - gfx/hud/sb_flag_blue_taken.tga - gfx/hud/sb_flag_red_carrying.tga - gfx/hud/sb_flag_red_lost.tga - gfx/hud/sb_flag_red_shielded.tga - gfx/hud/sb_flag_red_taken.tga - gfx/hud/sb_fuel.tga - gfx/hud/sb_health.tga - gfx/hud/sb_highlight_1.tga - gfx/hud/sb_highlight_2.tga - gfx/hud/sb_highlight_3.tga - gfx/hud/sb_highlight_4.tga - gfx/hud/sb_invinc.tga - gfx/hud/sb_kh_blue.tga - gfx/hud/sb_kh_pink.tga - gfx/hud/sb_kh_red.tga - gfx/hud/sb_kh_yellow.tga - gfx/hud/sb_nexball_carrying.tga - gfx/hud/sb_rocket.tga - gfx/hud/sb_scoreboard_bg.tga - gfx/hud/sb_scoreboard_tableheader.tga - gfx/hud/sb_shells.tga - gfx/hud/sb_str.tga - gfx/hud/sb_timerbg.tga - models/ctf/shield.md3 - models/ctf/shockwavetransring.md3 - models/gibs/arm.md3 - models/gibs/arm.md3_0.skin - models/gibs/arm.md3_1.skin - models/gibs/arm.md3_2.skin - models/gibs/bloodyskull.md3 - models/gibs/bloodyskull.md3_0.skin - models/gibs/bloodyskull.md3_1.skin - models/gibs/bloodyskull.md3_2.skin - models/gibs/chest.md3 - models/gibs/chest.md3_0.skin - models/gibs/chest.md3_1.skin - models/gibs/chest.md3_2.skin - models/gibs/chunk.mdl - models/gibs/eye.md3 - models/gibs/leg1.md3 - models/gibs/leg1.md3_0.skin - models/gibs/leg1.md3_1.skin - models/gibs/leg1.md3_2.skin - models/gibs/leg2.md3 - models/gibs/leg2.md3_0.skin - models/gibs/leg2.md3_1.skin - models/gibs/leg2.md3_2.skin - models/gibs/smallchest.md3 - models/gibs/smallchest.md3_0.skin - models/gibs/smallchest.md3_1.skin - models/gibs/smallchest.md3_2.skin - models/nexball/ball.md3 - models/onslaught/boom.md3 - models/onslaught/controlpoint_icon_dmg1.md3 - models/onslaught/controlpoint_icon_dmg2.md3 - models/onslaught/controlpoint_icon_dmg3.md3 - models/onslaught/controlpoint_icon_gib1.md3 - models/onslaught/controlpoint_icon_gib2.md3 - models/onslaught/controlpoint_icon_gib4.md3 - models/onslaught/controlpoint_pad2.md3 - models/onslaught/generator_dead.md3 - models/onslaught/generator_dmg1.md3 - models/onslaught/generator_dmg2.md3 - models/onslaught/generator_dmg3.md3 - models/onslaught/generator_dmg4.md3 - models/onslaught/generator_dmg5.md3 - models/onslaught/generator_dmg6.md3 - models/onslaught/generator_dmg7.md3 - models/onslaught/generator_dmg8.md3 - models/onslaught/generator_dmg9.md3 - models/onslaught/gen_gib1.md3 - models/onslaught/gen_gib2.md3 - models/onslaught/gen_gib3.md3 - models/onslaught/ons_ray.md3 - models/onslaught/shockwave.md3 - models/onslaught/shockwavetransring.md3 - models/sprites/as-defend_frame0.tga - models/sprites/as-destroy_frame0.tga - models/sprites/as-push_frame0.tga - models/sprites/bluebase_frame0.tga - models/sprites/bluebase.tga - models/sprites/danger_frame0.tga - models/sprites/danger.tga - models/sprites/defend.tga - models/sprites/destroy.tga - models/sprites/dom-blue_frame0.tga - models/sprites/dom-neut_frame0.tga - models/sprites/dom-pink_frame0.tga - models/sprites/dom-red_frame0.tga - models/sprites/dom-yellow_frame0.tga - models/sprites/flagcarrier_frame0.tga - models/sprites/flagcarrier.tga - models/sprites/helpme_frame0.tga - models/sprites/helpme.tga - models/sprites/here_frame0.tga - models/sprites/here.tga - models/sprites/item-extralife_frame0.tga - models/sprites/item-extralife_frame1.tga - models/sprites/item-fuelregen_frame0.tga - models/sprites/item-fuelregen_frame1.tga - models/sprites/item-invis_frame0.tga - models/sprites/item-invis_frame1.tga - models/sprites/item-jetpack_frame0.tga - models/sprites/item-jetpack_frame1.tga - models/sprites/item-shield_frame0.tga - models/sprites/item-shield_frame1.tga - models/sprites/item-speed_frame0.tga - models/sprites/item-speed_frame1.tga - models/sprites/item-strength_frame0.tga - models/sprites/item-strength_frame1.tga - models/sprites/keycarrier-blue_frame0.tga - models/sprites/keycarrier-blue.tga - models/sprites/keycarrier-finish_frame0.tga - models/sprites/keycarrier-finish.tga - models/sprites/keycarrier-friend_frame0.tga - models/sprites/keycarrier-friend.tga - models/sprites/keycarrier-pink_frame0.tga - models/sprites/keycarrier-pink.tga - models/sprites/keycarrier-red_frame0.tga - models/sprites/keycarrier-red.tga - models/sprites/keycarrier-yellow_frame0.tga - models/sprites/keycarrier-yellow.tga - models/sprites/key-dropped_frame0.tga - models/sprites/key-dropped.tga - models/sprites/nb-ball_frame0.tga - models/sprites/ons-cp-atck-blue_frame0.tga - models/sprites/ons-cp-atck-blue_frame1.tga - models/sprites/ons-cp-atck-neut_frame0.tga - models/sprites/ons-cp-atck-neut_frame1.tga - models/sprites/ons-cp-atck-red_frame0.tga - models/sprites/ons-cp-atck-red_frame1.tga - models/sprites/ons-cp-blue_frame0.tga - models/sprites/ons-cp-blue.tga - models/sprites/ons-cp-dfnd-blue_frame0.tga - models/sprites/ons-cp-dfnd-blue_frame1.tga - models/sprites/ons-cp-dfnd-red_frame0.tga - models/sprites/ons-cp-dfnd-red_frame1.tga - models/sprites/ons-cp-neut_frame0.tga - models/sprites/ons-cp-neut.tga - models/sprites/ons-cp-red_frame0.tga - models/sprites/ons-cp-red.tga - models/sprites/ons-gen-blue_frame0.tga - models/sprites/ons-gen-blue.tga - models/sprites/ons-gen-red_frame0.tga - models/sprites/ons-gen-red.tga - models/sprites/ons-gen-shielded_frame0.tga - models/sprites/ons-gen-shielded.tga - models/sprites/push.tga - models/sprites/race-checkpoint_frame0.tga - models/sprites/race-checkpoint.tga - models/sprites/race-finish_frame0.tga - models/sprites/race-finish.tga - models/sprites/race-start_frame0.tga - models/sprites/redbase_frame0.tga - models/sprites/redbase.tga - models/sprites/waypoint_frame0.tga - models/sprites/waypoint.tga - models/sprites/wpn-campingrifle_frame0.tga - models/sprites/wpn-crylink_frame0.tga - models/sprites/wpn-electro_frame0.tga - models/sprites/wpn-gl_frame0.tga - models/sprites/wpn-hagar_frame0.tga - models/sprites/wpn-hlac_frame0.tga - models/sprites/wpn-hookgun_frame0.tga - models/sprites/wpn-laser_frame0.tga - models/sprites/wpn-minstanex_frame0.tga - models/sprites/wpn-nex_frame0.tga - models/sprites/wpn-porto_frame0.tga - models/sprites/wpn-rl_frame0.tga - models/sprites/wpn-shotgun_frame0.tga - models/sprites/wpn-uzi_frame0.tga - models/weapons/g_tuba.md3 - models/weapons/h_tuba.dpm - models/weapons/v_tuba.md3 - particles/particlefont.tga - scripts/onslaught.shader - scripts/tuba.shader - sound/announcer/male/amazing.ogg - sound/announcer/male/awesome.ogg - sound/ctf/blue_capture.wav - sound/ctf/blue_dropped.wav - sound/ctf/blue_returned.wav - sound/ctf/blue_taken.wav - sound/ctf/flag_respawn.wav - sound/ctf/red_capture.wav - sound/ctf/red_dropped.wav - sound/ctf/red_returned.wav - sound/ctf/red_taken.wav - sound/misc/armor10.wav - sound/misc/armor17_5.wav - sound/misc/armor1.wav - sound/misc/armor25.wav - sound/misc/itemrespawncountdown.ogg - sound/misc/poweroff.wav - sound/misc/powerup.ogg - sound/misc/shield_respawn.wav - sound/misc/strength_respawn.wav - sound/nexball/bounce.ogg - sound/nexball/drop.ogg - sound/nexball/shoot1.wav - sound/nexball/shoot2.ogg - sound/nexball/steal.ogg - sound/onslaught/electricity_explode.ogg - sound/onslaught/ons_hit1.ogg - sound/onslaught/ons_hit2.ogg - sound/onslaught/ons_spark1.ogg - sound/onslaught/ons_spark2.ogg - sound/onslaught/shockwave.ogg - sound/player/pyria-skadi/coms/needhelp2.ogg - sound/weapons/nexwhoosh1.ogg - sound/weapons/nexwhoosh2.ogg - sound/weapons/nexwhoosh3.ogg - sound/weapons/tuba_note0.ogg - sound/weapons/tuba_note-10.ogg - sound/weapons/tuba_note10.ogg - sound/weapons/tuba_note-11.ogg - sound/weapons/tuba_note11.ogg - sound/weapons/tuba_note-12.ogg - sound/weapons/tuba_note12.ogg - sound/weapons/tuba_note-13.ogg - sound/weapons/tuba_note13.ogg - sound/weapons/tuba_note-14.ogg - sound/weapons/tuba_note14.ogg - sound/weapons/tuba_note-15.ogg - sound/weapons/tuba_note15.ogg - sound/weapons/tuba_note-16.ogg - sound/weapons/tuba_note16.ogg - sound/weapons/tuba_note-17.ogg - sound/weapons/tuba_note17.ogg - sound/weapons/tuba_note-18.ogg - sound/weapons/tuba_note18.ogg - sound/weapons/tuba_note19.ogg - sound/weapons/tuba_note-1.ogg - sound/weapons/tuba_note1.ogg - sound/weapons/tuba_note20.ogg - sound/weapons/tuba_note21.ogg - sound/weapons/tuba_note22.ogg - sound/weapons/tuba_note23.ogg - sound/weapons/tuba_note24.ogg - sound/weapons/tuba_note25.ogg - sound/weapons/tuba_note26.ogg - sound/weapons/tuba_note27.ogg - sound/weapons/tuba_note-2.ogg - sound/weapons/tuba_note2.ogg - sound/weapons/tuba_note-3.ogg - sound/weapons/tuba_note3.ogg - sound/weapons/tuba_note-4.ogg - sound/weapons/tuba_note4.ogg - sound/weapons/tuba_note-5.ogg - sound/weapons/tuba_note5.ogg - sound/weapons/tuba_note-6.ogg - sound/weapons/tuba_note6.ogg - sound/weapons/tuba_note-7.ogg - sound/weapons/tuba_note7.ogg - sound/weapons/tuba_note-8.ogg - sound/weapons/tuba_note8.ogg - sound/weapons/tuba_note-9.ogg - sound/weapons/tuba_note9.ogg - sound/weapons/unavailable.wav - sound/weapons/weaponpickup.ogg - textures/bloodyskull_alien_glow.tga - textures/bloodyskull_alien.tga - textures/bloodyskull.jpg - textures/bloodyskull_robot_gloss.tga - textures/bloodyskull_robot_glow.tga - textures/bloodyskull_robot.tga - textures/generator_destroyed.tga - textures/generator_lightning2.tga - textures/generator_lightning.tga - textures/generator.tga - textures/meat_alien_gloss.tga - textures/meat_alien_glow.tga - textures/meat_alien_norm.tga - textures/meat_alien.tga - textures/meat_gloss.tga - textures/meat_norm.tga - textures/meat_robot_gloss.tga - textures/meat_robot_glow.tga - textures/meat_robot_norm.tga - textures/meat_robot.tga - textures/meat.tga - textures/nexball/ball_gloss.tga - textures/nexball/ball_norm.tga - textures/nexball/ball.tga - textures/ons_boom1.tga - textures/ons_gengib.tga - textures/ons_icon.tga - textures/ons_icon_thrust.tga - textures/ons_pad.tga - textures/ons_ray.tga - textures/ons_shockwave1.tga - textures/ons_shockwave2.tga - textures/ons_smoke1.tga - textures/ons_text.tga - textures/tuba_gloss.tga - textures/tuba_glow.tga - textures/tuba.tga - sound/weapons/fireball_fire2.wav - sound/weapons/fireball_fire.wav - sound/weapons/fireball_fly2.wav - sound/weapons/fireball_fly.wav - sound/weapons/fireball_impact2.wav - sound/weapons/fireball_prefire2.wav - models/weapons/g_fireball.md3 - models/weapons/h_fireball.dpm - models/weapons/h_fireball.dpm.framegroups - models/weapons/v_fireball.md3 - textures/fireball_gloss.tga - textures/fireball_glow.tga - textures/fireball.tga - models/sphere/sphere.md3 - models/sphere/sphere.tga - textures/nutsandbolts1_gloss.tga - textures/nutsandbolts1.tga - textures/nutsandbolts3_gloss.tga - textures/nutsandbolts3.tga - textures/nutsandbolts4_gloss.tga - textures/nutsandbolts4.tga - textures/nutsandbolts5_gloss.tga - textures/nutsandbolts5.tga - models/gibs/robo1.md3 - models/gibs/robo1.md3_0.skin - models/gibs/robo1.md3_1.skin - models/gibs/robo2.md3 - models/gibs/robo2.md3_0.skin - models/gibs/robo2.md3_1.skin - models/gibs/robo3.md3 - models/gibs/robo3.md3_0.skin - models/gibs/robo3.md3_1.skin - models/gibs/robo4.md3 - models/gibs/robo4.md3_0.skin - models/gibs/robo4.md3_1.skin - models/gibs/robo5.md3 - models/gibs/robo6.md3 - models/gibs/robo7.md3 - models/gibs/robo7.md3_0.skin - models/gibs/robo7.md3_1.skin - models/gibs/robo8.md3 - models/gibs/robo8.md3_0.skin - models/gibs/robo8.md3_1.skin - models/gibs/robo.md3 - models/gibs/robo.md3_0.skin - models/gibs/robo.md3_1.skin -" - -rm -rf pack -mkdir pack -for F in $COMPAT_FILES; do - case "$F" in - */*) - mkdir -p pack/${F%/*} - ;; - esac - cp "$F" pack/"$F" -done - -cd pack - -find textures/ -type f -print0 | qual=85 scaledown=256x256 xargs -0 ../../misc/tools/jpeg-if-not-alpha.sh - -if false; then - find . -name \*.ogg | while IFS= read -r NAME; do - c=`vorbiscomment -l "$NAME"` - oggdec -o "$NAME.wav" "$NAME" - oggenc -q 0 -o "$NAME" "$NAME.wav" - echo "$c" | vorbiscomment -w "$NAME" - rm -f "$NAME.wav" - touch "${NAME%.ogg}.wav" # to disable this file, should the client have it - done -fi - -rev=`svnversion .. | sed 's/M$//g; s/.*://g;'` -pack="zzz_svn-compat-$rev" -echo "Support files to play on svn servers of revision $rev" > "$pack.txt" -7za a -tzip -mx=9 "../$pack.pk3" . -rm -f "$pack.txt" - -cd .. -rm -rf pack diff --git a/commands.cfg b/commands.cfg index 174a458347..ab737d33e3 100644 --- a/commands.cfg +++ b/commands.cfg @@ -81,6 +81,7 @@ alias addvote "qc_cmd_svmenu addtolist sv_vote_commands ${* ?}" alias bsp "ls maps/*.bsp" alias chmap "changelevel ${* ?}" alias clearmap "disconnect" +alias devmap "set _developer_save $developer; set developer 1; changelevel ${* ?}; set developer $_developer_save" alias ply "playdemo $1" alias rec "record demos/${1 !}" alias search "apropos ${* ?}" @@ -109,6 +110,7 @@ alias menu_loadmap_prepare "disconnect; wait; g_campaign 0; menu_cmd rpn /_menu_ // ========================================================== // commented out commands are really only intended for internal use alias blurtest "qc_cmd_cl blurtest ${* ?}" // Feature for testing blur postprocessing +alias create_scrshot_ent "qc_cmd_cl create_scrshot_ent ${* ?}" // Create an entity at this location for automatic screenshots alias debugmodel "qc_cmd_cl debugmodel ${* ?}" // Spawn a debug model manually //alias handlevote "qc_cmd_cl handlevote ${* ?}" // System to handle selecting a vote or option alias hud "qc_cmd_cl hud ${* ?}" // Commands regarding/controlling the HUD system @@ -239,6 +241,47 @@ alias unban "qc_cmd_sv unban ${* ?}" // Remove // other aliases for ban commands alias bans "banlist" +// character classes (intersected with 32..126 minus ", $, ;, ^, \ - if you +// want these, include them explicitly) +// note that QC code always forbids $ and ; in VoteCommand_checknasty +set _iscntrl "" +set _isblank " " +set _ispunct "!#%&'()*+,-./:<=>?@[]_`{|}~" +set _isdigit "0123456789" +set _isupper "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +set _islower "abcdefghijklmnopqrstuvwxyz" +set _isxdigit "0123456789abcdefABCDEF" + +// derived character classes +set _isalpha "$_isupper$_islower" +set _isalnum "$_isalpha$_isdigit" +set _isgraph "$_ispunct$_isalnum" +set _isascii "$_isgraph$_isblank" +set _isprint "$_isgraph$_isblank" +set _isspace "$_isblank" + +// restriction is specified as followed by instances of ';' +// and the optional character class to verify the argument by (no checking if +// empty) +// set cvar to empty string to not check the command at all +// if cvar is not set there will be a warning +set sv_vote_command_restriction_restart "0" +set sv_vote_command_restriction_fraglimit "1;$_isdigit" +set sv_vote_command_restriction_chmap "1;$_isgraph" +set sv_vote_command_restriction_gotomap "1;$_isgraph" +set sv_vote_command_restriction_nextmap "1;$_isgraph" +set sv_vote_command_restriction_endmatch "0" +set sv_vote_command_restriction_reducematchtime "0" +set sv_vote_command_restriction_extendmatchtime "0" +set sv_vote_command_restriction_allready "0" +set sv_vote_command_restriction_kick "1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" // enough space for ban reason +set sv_vote_command_restriction_kickban "1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" // enough space for ban reason +set sv_vote_command_restriction_cointoss "0" +set sv_vote_command_restriction_movetoauto "1;" +set sv_vote_command_restriction_movetored "1;" +set sv_vote_command_restriction_movetoblue "1;" +set sv_vote_command_restriction_movetoyellow "1;" +set sv_vote_command_restriction_movetopink "1;" // ================================= // voting - server/command/vote.qc diff --git a/cpp.cfg b/cpp.cfg new file mode 100644 index 0000000000..3a190533ea --- /dev/null +++ b/cpp.cfg @@ -0,0 +1,42 @@ +alias _dont "" +alias _do "$*" + +set _ifstack "" +alias # "$_ifstack $*" + +alias #ifeq "set \"_ifnew_$1\" _dont; set \"_ifnew_$2\" _do; _ifeq_2 \"_ifnew_$1\"" +alias #ifneq "set \"_ifnew_$1\" _do; set \"_ifnew_$2\" _dont; _ifeq_2 \"_ifnew_$1\"" +alias _ifeq_2 "set _ifstack \"${$1} $_ifstack\"" +alias #else "_else_2$_ifstack" +alias _else_2_do "set _ifstack \"_dont ${* q?}\"" +alias _else_2_dont "set _ifstack \"_do ${* q?}\"" +alias #endif "_endif_2 $_ifstack" +alias _endif_2 "set _ifstack \"${2- q?}\"" + +alias #ifdef "#ifneq \"${$1 ?}\" \"\"" +alias #ifndef "#ifeq \"${$1 ?}\" \"\"" + +alias #include "# exec $*" +alias #define "# set $*" +alias #undef "# unset $*" +alias #error "# echo ERROR: $*; # quit" +alias #warning "# echo WARNING: $*" + +// EXAMPLE: +// #ifeq "$a" "$b" +// #ifeq "$a" "$c" +// # echo "a == b == c" +// #else +// # echo "a == b != c" +// #endif +// #else +// #ifeq "$a" "$c" +// # echo "a == c != b" +// #else +// #ifeq "$b" "$c" +// # echo "b == c != a" +// #else +// # echo "a != b != c != a" +// #endif +// #endif +// #endif diff --git a/crosshairs.cfg b/crosshairs.cfg index 7385853b16..5e071a5874 100644 --- a/crosshairs.cfg +++ b/crosshairs.cfg @@ -27,6 +27,7 @@ seta crosshair_pickup_speed 4 // hit indication animation settings seta crosshair_hitindication 0.5 seta crosshair_hitindication_color "10 -10 -10" +seta crosshair_hitindication_per_weapon_color "10 10 10" seta crosshair_hitindication_speed 5 // hit testing/tracing for special effects for the crosshair @@ -152,4 +153,4 @@ seta crosshair_ring_hagar_alpha 0.15 // reload ring seta crosshair_ring_reload 1 "main cvar to enable or disable ammo crosshair rings" seta crosshair_ring_reload_size 2.5 "reload ring size" -seta crosshair_ring_reload_alpha 0.2 "reload ring alpha" \ No newline at end of file +seta crosshair_ring_reload_alpha 0.2 "reload ring alpha" diff --git a/ctfscoring-ai.cfg b/ctfscoring-ai.cfg index 7113736478..284aff7c5b 100644 --- a/ctfscoring-ai.cfg +++ b/ctfscoring-ai.cfg @@ -1,19 +1,10 @@ -set g_ctf_personalscore_pickup_base 1 -set g_ctf_personalscore_pickup_dropped_early 0 -set g_ctf_personalscore_pickup_dropped_late 1 -set g_ctf_personalscore_capture 20 -set g_ctf_personalscore_kill 5 -set g_ctf_personalpenalty_drop 0 -set g_ctf_personalpenalty_suicidedrop 1 -set g_ctf_personalpenalty_returned 1 -set g_ctf_personalscore_return 5 -set g_ctf_personalscore_return_rogue 3 -set g_ctf_personalscore_return_by_killer 5 -set g_ctf_personalscore_return_rogue_by_killer 5 -// AWIN = 21 -// AFAIL = 0 -// AFAILVOID = 1 -// DWIN = 10 -// ARETRY = -1..0 -// DRETRY = 5 -// ATAKE = 1 +set g_ctf_score_capture 20 +set g_ctf_score_capture_assist 0 +set g_ctf_score_kill 5 +set g_ctf_score_penalty_drop 0 +set g_ctf_score_penalty_suicidedrop 1 +set g_ctf_score_penalty_returned 1 +set g_ctf_score_pickup_base 1 +set g_ctf_score_pickup_dropped_early 0 +set g_ctf_score_pickup_dropped_late 1 +set g_ctf_score_return 5 diff --git a/ctfscoring-alien.cfg b/ctfscoring-alien.cfg deleted file mode 100644 index dd5758d495..0000000000 --- a/ctfscoring-alien.cfg +++ /dev/null @@ -1,19 +0,0 @@ -set g_ctf_personalscore_pickup_base 1 -set g_ctf_personalscore_pickup_dropped_early 1 -set g_ctf_personalscore_pickup_dropped_late 1 -set g_ctf_personalscore_capture 30 -set g_ctf_personalscore_kill 1 -set g_ctf_personalpenalty_drop 2 -set g_ctf_personalpenalty_suicidedrop 2 -set g_ctf_personalpenalty_returned 0 -set g_ctf_personalscore_return 5 -set g_ctf_personalscore_return_rogue 10 -set g_ctf_personalscore_return_by_killer 6 -set g_ctf_personalscore_return_rogue_by_killer 11 -// AWIN = 31 -// AFAIL = -1 -// AFAILVOID = 1 -// DWIN = 6..7 -// ARETRY = -1 -// DRETRY = 1 -// ATAKE = 1 diff --git a/ctfscoring-alpha.cfg b/ctfscoring-alpha.cfg deleted file mode 100644 index c15cee8dab..0000000000 --- a/ctfscoring-alpha.cfg +++ /dev/null @@ -1,19 +0,0 @@ -set g_ctf_personalscore_pickup_base 0 -set g_ctf_personalscore_pickup_dropped_early 0 -set g_ctf_personalscore_pickup_dropped_late 0 -set g_ctf_personalscore_capture 20 -set g_ctf_personalscore_kill 0 -set g_ctf_personalpenalty_drop 0 -set g_ctf_personalpenalty_suicidedrop 0 -set g_ctf_personalpenalty_returned 0 -set g_ctf_personalscore_return 5 -set g_ctf_personalscore_return_rogue 10 -set g_ctf_personalscore_return_by_killer 5 -set g_ctf_personalscore_return_rogue_by_killer 10 -// AWIN = 20 -// AFAIL = 0 -// AFAILVOID = 0 -// DWIN = 5 -// ARETRY = 0 -// DRETRY = 0 -// ATAKE = 0 diff --git a/ctfscoring-div0.cfg b/ctfscoring-div0.cfg index 33c8ab1b72..f361d18952 100644 --- a/ctfscoring-div0.cfg +++ b/ctfscoring-div0.cfg @@ -1,19 +1,10 @@ -set g_ctf_personalscore_pickup_base 0 -set g_ctf_personalscore_pickup_dropped_early 1 -set g_ctf_personalscore_pickup_dropped_late 1 -set g_ctf_personalscore_capture 25 -set g_ctf_personalscore_kill 3 -set g_ctf_personalpenalty_drop 2 -set g_ctf_personalpenalty_suicidedrop 2 -set g_ctf_personalpenalty_returned 1 -set g_ctf_personalscore_return 2 // lowered so it's better if the killer does the return -set g_ctf_personalscore_return_rogue 10 -set g_ctf_personalscore_return_by_killer 5 -set g_ctf_personalscore_return_rogue_by_killer 10 -// AWIN = 25 -// AFAIL = -3 -// AFAILVOID = -2 -// DWIN = 8 (5 if someone else returned) -// ARETRY = -1 -// DRETRY = 3 -// ATAKE = 0 +set g_ctf_score_capture 25 +set g_ctf_score_capture_assist 0 +set g_ctf_score_kill 3 +set g_ctf_score_penalty_drop 2 +set g_ctf_score_penalty_suicidedrop 2 +set g_ctf_score_penalty_returned 1 +set g_ctf_score_pickup_base 0 +set g_ctf_score_pickup_dropped_early 1 +set g_ctf_score_pickup_dropped_late 1 +set g_ctf_score_return 2 // lowered so it's better if the killer does the return diff --git a/ctfscoring-nex242.cfg b/ctfscoring-nex242.cfg index 861ead4613..7461dffc00 100644 --- a/ctfscoring-nex242.cfg +++ b/ctfscoring-nex242.cfg @@ -1,19 +1,10 @@ -set g_ctf_personalscore_pickup_base 1 -set g_ctf_personalscore_pickup_dropped_early 1 -set g_ctf_personalscore_pickup_dropped_late 1 -set g_ctf_personalscore_capture 20 -set g_ctf_personalscore_kill 1 -set g_ctf_personalpenalty_drop 0 -set g_ctf_personalpenalty_suicidedrop 1 -set g_ctf_personalpenalty_returned 0 -set g_ctf_personalscore_return 5 -set g_ctf_personalscore_return_rogue 10 -set g_ctf_personalscore_return_by_killer 5 -set g_ctf_personalscore_return_rogue_by_killer 10 -// AWIN = 21 -// AFAIL = 1 -// AFAILVOID = 1 -// DWIN = 6 -// ARETRY = 1 -// DRETRY = 1 -// ATAKE = 1 +set g_ctf_score_capture 20 +set g_ctf_score_capture_assist 0 +set g_ctf_score_kill 1 +set g_ctf_score_penalty_drop 0 +set g_ctf_score_penalty_suicidedrop 1 +set g_ctf_score_penalty_returned 0 +set g_ctf_score_pickup_base 1 +set g_ctf_score_pickup_dropped_early 1 +set g_ctf_score_pickup_dropped_late 1 +set g_ctf_score_return 5 diff --git a/ctfscoring-samual.cfg b/ctfscoring-samual.cfg new file mode 100644 index 0000000000..9bc87c2c5c --- /dev/null +++ b/ctfscoring-samual.cfg @@ -0,0 +1,10 @@ +set g_ctf_score_capture 20 +set g_ctf_score_capture_assist 10 +set g_ctf_score_kill 5 +set g_ctf_score_penalty_drop 1 +set g_ctf_score_penalty_suicidedrop 1 +set g_ctf_score_penalty_returned 1 +set g_ctf_score_pickup_base 1 +set g_ctf_score_pickup_dropped_early 1 +set g_ctf_score_pickup_dropped_late 1 +set g_ctf_score_return 10 diff --git a/ctfscoring-z-lowdeposit.cfg b/ctfscoring-z-lowdeposit.cfg deleted file mode 100644 index c75b306806..0000000000 --- a/ctfscoring-z-lowdeposit.cfg +++ /dev/null @@ -1,19 +0,0 @@ -set g_ctf_personalscore_pickup_base -1 -set g_ctf_personalscore_pickup_dropped_early 5 -set g_ctf_personalscore_pickup_dropped_late 9 -set g_ctf_personalscore_capture 26 -set g_ctf_personalscore_kill 5 -set g_ctf_personalpenalty_drop 9 -set g_ctf_personalpenalty_suicidedrop 9 -set g_ctf_personalpenalty_returned 0 -set g_ctf_personalscore_return 3 -set g_ctf_personalscore_return_rogue 10 -set g_ctf_personalscore_return_by_killer 3 -set g_ctf_personalscore_return_rogue_by_killer 10 -// AWIN = 25 -// AFAIL = -10 -// AFAILVOID = -10 -// DWIN = 8 -// ARETRY = -1..-4 -// DRETRY = 5 -// ATAKE = -1 diff --git a/ctfscoring-z.cfg b/ctfscoring-z.cfg deleted file mode 100644 index e45bdd93a3..0000000000 --- a/ctfscoring-z.cfg +++ /dev/null @@ -1,19 +0,0 @@ -set g_ctf_personalscore_pickup_base -5 -set g_ctf_personalscore_pickup_dropped_early 1 -set g_ctf_personalscore_pickup_dropped_late 5 -set g_ctf_personalscore_capture 30 -set g_ctf_personalscore_kill 5 -set g_ctf_personalpenalty_drop 5 -set g_ctf_personalpenalty_suicidedrop 5 -set g_ctf_personalpenalty_returned 0 -set g_ctf_personalscore_return 3 -set g_ctf_personalscore_return_rogue 10 -set g_ctf_personalscore_return_by_killer 3 -set g_ctf_personalscore_return_rogue_by_killer 10 -// AWIN = 25 -// AFAIL = -10 -// AFAILVOID = -10 -// DWIN = 8 -// ARETRY = -1..-4 -// DRETRY = 5 -// ATAKE = -5 diff --git a/defaultXDF.cfg b/defaultXDF.cfg new file mode 100644 index 0000000000..f5a5442e3b --- /dev/null +++ b/defaultXDF.cfg @@ -0,0 +1,28 @@ +// ================ +// Xonotic Defrag +// ================ + +exec defaultXonotic.cfg +exec balanceXDF.cfg +exec physicsXDF.cfg + +// general gameplay +set g_jump_grunt 1 // make enemies even easier to hear when they're jumping around +set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly) +set g_balance_kill_antispam 0 +set g_forced_respawn 1 +set g_jump_grunt 1 +// g_playerclip_collisions 0 // do not check playerclips +set g_powerups 0 // set to -1 or patch xonotic +set g_spawnpoints_auto_move_out_of_solid 1 +set g_start_delay 3 +set g_use_ammunition 0 "if set to 0 all weapons have unlimited ammunition" +set g_weapon_stay 1 "1: ghost weapons can be picked up too but give no ammo, 2: ghost weapons refill ammo to one pickup size, thrown guns have no ammo" +set teamplay_mode 2 // friendly fire and self damage +set sv_vote_nospectators 1 +set timelimit_override 20 + +// game mode settings +set g_cts_finish_kill_delay 2 +set g_cts_respawn_delay 0 +set g_cts_selfdamage 0 diff --git a/defaultXPM.cfg b/defaultXPM.cfg index 38d74c7c99..0e96e08848 100644 --- a/defaultXPM.cfg +++ b/defaultXPM.cfg @@ -1,31 +1,21 @@ -// Xonotic ProMode -exec defaultXonotic.cfg - -//============== -// pure changes -//============== - -// players -sv_fbskin_green // visible playermodel forced on everyone -set teamplay_mode 2 // friendly fire and self damage - -//================ -// impure changes -//================ +// ================== +// Xonotic Pro-Mode +// ================== -// players -g_jump_grunt 1 // make enemies even easier to hear when they're jumping around - -// physics -exec physicsXPM.cfg // XPM physics. Similar to vanilla Xonotic physics, with a different way to accelerate: through strafejumping +exec defaultXonotic.cfg +exec balanceXPM.cfg -// balance -set g_balance_weaponswitchdelay 0 // no switch animation, this is standard in "pro modes" ;) +// general gameplay +set g_norecoil 1 set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly) - -// match rules -set timelimit_overtimes 1 // overtimes on, draw matches are less interesting! :) -set g_forced_respawn 1 // no delaying/cheating a match by not spawning - -// info -set sv_fraginfo_stats 0 // don't reveal how much health/armor the attacker had +set g_balance_kill_antispam 0 +set g_forced_respawn 1 +set teamplay_mode 2 // friendly fire and self damage +set sv_vote_nospectators 1 +set g_chat_nospectators 2 +set g_warmup 1 +set g_balance_teams 0 +set g_spawnshieldtime 0 +set sv_autoscreenshot 1 +set sv_ready_restart 1 +set sv_ready_restart_after_countdown 1 diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 0ec01d5825..9796e60027 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -31,15 +31,6 @@ seta cl_startcount 0 "how many times the client has been run" seta g_configversion 0 "Configuration file version (used to upgrade settings) 0: first run, or previous start was <2.4.1 Later, it's overridden by config.cfg, version ranges are defined in config_update.cfg" -// say aliases -alias asay_ctf_flagcarrier "say_team flag carrier at %y" -alias asay_ctf_haveflag "say_team (%l) have the flag" -alias asay_willgo "say_team will go to %y" -alias asay_support "say_team (%l) need help, %h%%" -alias asay_killed "say_team got killed at %d" -alias asay_noammo "say_team (%l) need %W for %w" -alias asay_drop "say_team (%l) dropped %w ; impulse 17" - // other aliases alias +hook +button6 alias -hook -button6 @@ -57,7 +48,7 @@ bind f6 team_auto mod_q3bsp_lightmapmergepower 4 // player defaults -_cl_color 112 +_cl_color "112.211" // same effect as 112, but menuqc can detect this as the default and not intentionally set _cl_name Player _cl_playermodel models/player/erebus.iqm _cl_playerskin 0 @@ -146,6 +137,14 @@ gl_polyblend 0 // whether to use screen tints, this has now been replaced by a b r_motionblur 0 // motion blur value, default is 0 r_damageblur 0 // motion blur when damaged, default is 0 (removed in Xonotic) +r_bloom_blur 4 +r_bloom_brighten 2 +r_bloom_colorexponent 1 +r_bloom_colorscale 1 +r_bloom_colorsubtract 0.125 +r_bloom_resolution 320 +r_bloom_scenebrightness 0.85 + seta vid_x11_display "" "xonotic-linux-*.sh will use this to start xonotic on an other/new X display" // This can have three possible settings: // "" run as usual @@ -455,45 +454,6 @@ set sv_dodging_height_threshold 10 "the maximum height above ground where to all set sv_dodging_wall_distance_threshold 10 "the maximum distance from a wall that still allows dodging" set sv_dodging_sound 1 "if 1 dodging makes a sound. if 0 dodging is silent" -set leadlimit 0 -set leadlimit_and_fraglimit 0 "if set, leadlimit is ANDed with fraglimit (otherwise ORed)" - -// this means that timelimit can be overidden globally and fraglimit can be overidden for each game mode: DM/TDM, Domination, CTF, and Runematch. -seta timelimit_override -1 "Time limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta fraglimit_override -1 "Frag limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta leadlimit_override -1 "Lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta capturelimit_override -1 "Capture limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta captureleadlimit_override -1 "Capture llead imit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_arena_point_limit -1 "Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_arena_point_leadlimit -1 "Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_domination_point_limit -1 "Domination point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_domination_point_leadlimit -1 "Domination point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_runematch_point_limit -1 "Runematch point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_runematch_point_leadlimit -1 "Runematch point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_keyhunt_point_limit -1 "Keyhunt point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_keyhunt_point_leadlimit -1 "Keyhunt point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_race_laps_limit -1 "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_nexball_safepass_maxdist 5000 // Max distance to allow save fassping (0 to turn off safe passing) -seta g_nexball_safepass_turnrate 0.1 // How fast the safe-pass ball can habge direction -seta g_nexball_safepass_holdtime 0.75 // How long to remeber last teammate you pointed at -seta g_nexball_viewmodel_scale 0.25 // How large the ball for the carrier -seta g_nexball_viewmodel_offset "8 8 0" // Where the ball is located on carrier "forward right up" -seta g_nexball_tackling 1 // Allow ball theft? - - -seta g_ctf_ignore_frags 0 "1: regular frags give no points" - -set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them" -seta g_freezetag_warmup 5 "Time players get to run around before the round starts" -seta g_freezetag_point_limit -1 "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_freezetag_point_leadlimit -1 "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" -seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate" -seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range" -seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him" -seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with" - set g_spawn_furthest 0.5 "this amount of the spawns shall be far away from any players" set g_spawn_useallspawns 0 "use all spawns, e.g. also team spawns in non-teamplay, and all spawns, even enemy spawns, in teamplay" set g_spawn_near_teammate 0 "if set, players prefer spawns near a team mate" @@ -501,60 +461,6 @@ set g_spawn_near_teammate_distance 640 "max distance to consider a spawn to be n // respawn delay set g_respawn_delay 2 "number of seconds you have to wait before you can respawn again" set g_respawn_waves 0 "respawn in waves (every n seconds), intended to decrease overwhelming base attacks" -// when variables are set to 0, they take over the global setting... -// to force disable delay or waves, set them to 0.125 -set g_ctf_respawn_delay 0 -set g_ctf_respawn_waves 0 -set g_ctf_weapon_stay 0 -set g_dm_respawn_delay 0 -set g_dm_respawn_waves 0 -set g_dm_weapon_stay 0 -set g_dom_respawn_delay 0 -set g_dom_respawn_waves 0 -set g_dom_weapon_stay 0 -set g_lms_respawn_delay 0 -set g_lms_respawn_waves 0 -set g_lms_weapon_stay 0 -set g_rune_respawn_delay 0 -set g_rune_respawn_waves 0 -set g_rune_weapon_stay 0 -set g_tdm_respawn_delay 0 -set g_tdm_respawn_waves 0 -set g_tdm_weapon_stay 0 -set g_ka_respawn_delay 0 -set g_ka_respawn_waves 0 -set g_ka_weapon_stay 0 -set g_kh_respawn_delay 0 -set g_kh_respawn_waves 0 -set g_kh_weapon_stay 0 -set g_arena_respawn_delay 0 -set g_arena_respawn_waves 0 -set g_arena_weapon_stay 0 -set g_ca_respawn_delay 0 -set g_ca_respawn_waves 0 -set g_ca_weapon_stay 0 -set g_ca_damage2score_multiplier 0.01 -set g_ca_round_timelimit 180 -set g_nb_respawn_delay 0 -set g_nb_respawn_waves 0 -set g_nb_weapon_stay 0 -set g_as_respawn_delay 0 -set g_as_respawn_waves 0 -set g_as_weapon_stay 0 -set g_ons_respawn_delay 0 -set g_ons_respawn_waves 0 -set g_ons_weapon_stay 0 -set g_rc_respawn_waves 0 -set g_rc_respawn_delay 0 -set g_rc_weapon_stay 0 -set g_cts_respawn_waves 0 -set g_cts_respawn_delay 0 -set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts" -set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible" -set g_cts_weapon_stay 2 -set g_ft_respawn_waves 0 -set g_ft_respawn_delay 0 -set g_ft_weapon_stay 0 // overtime seta timelimit_overtime 2 "duration in minutes of one added overtime, added to the timelimit" @@ -574,215 +480,14 @@ seta g_friendlyfire_virtual_force 1 "for teamplay 4: apply force even though dam seta g_teamdamage_threshold 40 "for teamplay 4: threshold over which to apply mirror damage" seta g_teamdamage_resetspeed 20 "for teamplay 4: how fast player's teamdamage count decreases" -seta g_balance_teams 0 "automatically balance out players entering instead of asking them for their preferred team" -seta g_balance_teams_force 0 "automatically balance out teams when players move or disconnect" -seta g_balance_teams_prevent_imbalance 0 "prevent players from changing to larger teams" -set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)" -seta g_tdm_teams_override 0 "how many teams are in team deathmatch" -set g_tdm_team_spawns 0 "when 1, a map can define team spawnpoints for TDM" +seta g_balance_teams 1 "automatically balance out players entering instead of asking them for their preferred team" +seta g_balance_teams_prevent_imbalance 1 "prevent players from changing to larger teams" +set g_balance_teams_scorefactor 0.34 "at the end of the game, take score into account instead of team size by this amount (beware: values over 0.5 mean that a x:0 score imbalance will cause ALL new players to prefer the losing team at the end, despite numbers)" set g_changeteam_banned 0 "not allowed to change team" set g_changeteam_fragtransfer 0 "% of frags you get to keep when you change teams (rounded down)" set sv_teamnagger 1 "enable a nag message when the teams are unbalanced" -// dm -set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins" -set gamecfg 1 // "deathmatch" - -// ctf -set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score" -set g_ctf_flag_returntime 30 -set g_ctf_flagcarrier_selfdamage 1 -set g_ctf_flagcarrier_selfforce 1 -set g_ctf_fullbrightflags 0 -set g_ctf_dynamiclights 0 -set g_ctf_allow_drop 1 "dropping allows circumventing carrierkill score, so enable this with care!" -set g_ctf_reverse 0 "if enabled, flags positions are switched: you have to capture the enemy's flag from your own base by bringing it to your own flag in the enemy base" -set g_balance_ctf_delay_collect 1.0 -set g_balance_ctf_damageforcescale 1 - -set g_ctf_shield_max_ratio 0 "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)" -set g_ctf_shield_min_negscore 20 "shield the player from the flag if he's got this negative amount of points or less" -set g_ctf_shield_force 100 "push force of the shield" - -// fun for server admins -set g_ctf_flag_red_model "models/ctf/flags.md3" -set g_ctf_flag_red_skin 0 -set g_ctf_flag_blue_model "models/ctf/flags.md3" -set g_ctf_flag_blue_skin 1 -set g_ctf_flag_glowtrails 0 -set g_ctf_flag_pickup_effects 1 -set g_ctf_flag_capture_effects 1 -set g_ctf_captimerecord_always 0 "if enabled, assisted CTF records (with other players on the server) are recorded too" - -// runematch -set g_runematch 0 "Runematch: pick up and hold the runes, special items that give you points, a special power (rune) and a disadvantage (curse)" -set g_runematch_pointrate 5 -set g_runematch_fixedspawns 1 "use fixed runematch spawns if available" -set g_runematch_pointamt 1 -set g_runematch_shuffletime 30 "how often runes change position" -set g_runematch_respawntime 15 "how soon after being dropped to respawn" -set g_runematch_frags_killedby_runeholder 4 -set g_runematch_frags_killed_runeholder 5 -set g_runematch_frags_norune 0 -set g_runematch_drop_runes_max 2 "only drop up to 2 runes, the rest should respawn" -set g_runematch_allow_same 0 "allow matching rune-curse pairs" -set g_runematch_rune_alpha 0.78 -set g_runematch_rune_effects 544 "EF_ADDITIVE + EF_FULLBRIGHT = 544" -set g_runematch_rune_glow_size 0 -set g_runematch_rune_glow_color 0 -set g_runematch_rune_color_strength 1.0 -// strength/weakness -set g_balance_rune_strength_damage 2.0 -set g_balance_rune_strength_force 1.5 -set g_balance_curse_weak_damage 0.5 -set g_balance_curse_weak_force 0.6 -set g_balance_rune_strength_combo_damage 0.9 -set g_balance_rune_strength_combo_force 1.0 -// defense/vulner -set g_balance_rune_defense_takedamage 0.5 -set g_balance_curse_vulner_takedamage 2.0 -set g_balance_rune_defense_combo_takedamage 1.0 -// vampire/empathy -set g_balance_rune_vampire_absorb 0.4 -set g_balance_curse_empathy_takedamage -0.4 -set g_balance_rune_vampire_combo_absorb -0.1 -set g_balance_rune_vampire_maxhealth 500 -set g_balance_curse_empathy_minhealth 20 -set g_balance_rune_vampire_combo_minhealth 40 -// regen/venom -set g_balance_rune_regen_hpmod 1.75 -set g_balance_curse_venom_hpmod 0.6 -set g_balance_rune_regen_combo_hpmod 0.9 -set g_balance_rune_regen_regenrate 3.0 -set g_balance_curse_venom_rotrate 3.0 -set g_balance_rune_regen_combo_regenrate 0.5 -set g_balance_rune_regen_combo_rotrate 1.5 -set g_balance_rune_regen_limitmod 1 -set g_balance_curse_venom_limitmod 1 -set g_balance_rune_regen_combo_limitmod 1 -// speed/slow -set g_balance_rune_speed_atkrate 0.66 -set g_balance_curse_slow_atkrate 1.5 -set g_balance_rune_speed_combo_atkrate 1.2 -set g_balance_rune_speed_highspeed 1.5 -set g_balance_curse_slow_highspeed 0.6 -set g_balance_rune_speed_combo_highspeed 0.9 - -// domination -set g_domination 0 "Domination: capture and hold control points to gain points" -set g_domination_default_teams 2 "default number of teams for maps that aren't domination-specific" -seta g_domination_teams_override 0 "use a specific number of teams in domination games (minimum 2), disables dom_team entities" -set g_domination_disable_frags 0 "players can't get frags normally, only get points from kills" -set g_domination_point_amt 0 "override: how many points to get per ping" -set g_domination_point_fullbright 0 "domination point fullbright" -set g_domination_point_rate 0 "override: how often to give those points" -set g_domination_point_capturetime 0.1 "how long it takes to capture a point (given no interference)" -set g_domination_point_glow 0 "domination point glow (warning, slow)" -//set g_domination_balance_team_points 1 "# of points received is based on team sizes" - -// last man standing -set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins" -set g_lms_lives_override -1 -set g_lms_regenerate 0 -set g_lms_campcheck_interval 10 -set g_lms_campcheck_message "^1Don't camp!" -set g_lms_campcheck_damage 100 -set g_lms_campcheck_distance 1800 -set g_lms_last_join 3 "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives" -set g_lms_join_anytime 1 "if true, new players can join, but get same amount of lives as the worst player" - -// arena -set g_arena 0 "Arena: many one-on-one rounds are played to find the winner" -set g_arena_maxspawned 2 "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)" -set g_arena_roundbased 1 "if disabled, the next player will spawn as soon as someone dies" -set g_arena_warmup 5 "time, newly spawned players have to prepare themselves in round based matches" - -// ca -set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round." -set g_ca_point_limit 10 "point limit 10 is standard for clan arena" -set g_ca_point_leadlimit 0 -set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games." -set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts" - -// onslaught -set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it" -set g_onslaught_gen_health 2500 -set g_onslaught_cp_health 1000 -set g_onslaught_cp_buildhealth 100 -set g_onslaught_cp_buildtime 5 -set g_onslaught_cp_regen 20 - -// assault -set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend the base against the enemy for that time to win" - -// race -set g_race 0 "Race: be faster than your opponents" -set g_race_qualifying_timelimit 0 -set g_race_qualifying_timelimit_override -1 -set g_race_teams 0 "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)" - -// cts -set g_cts 0 "CTS: complete the stage" - -// nexball -set g_nexball 0 "Nexball: Basketball and Soccer go Xonotic" - -set g_nexball_basketball_effects_default 8 "default: dim light. The original version used 1024 (fire) but it gives bad performance" -set g_balance_nexball_primary_speed 1000 "launching speed" -set g_balance_nexball_primary_refire 0.7 "launching refire" -set g_balance_nexball_primary_animtime 0.3 "launching animtime" -set g_balance_nexball_secondary_animtime 0.3 "launching animtime" -set g_balance_nexball_secondary_speed 3000 "stealing projectile speed" -set g_balance_nexball_secondary_lifetime 0.15 "stealing projectile lifetime" -set g_balance_nexball_secondary_force 500 "stealing projectile force" -set g_balance_nexball_secondary_refire 0.6 "stealing projectile refire" -set g_balance_nexball_secondary_animtime 0.3 "stealing projectile animtime" - -// -1: MrBougo's first try, not very playable but working... -// The ball gets the player's velocity * 1.5 + a vertical boost -// 0: Revenant style -// Player's velocity + a boost where he's looking at + a boost -// perpendicularly to the first boost, that is upwards relatively -// to the view angle -// 1: MrBougo's modded Rev style 1 -// The 2nd Rev boost is always vertical -// 2: MrBougo's modded Rev style 2 -// The 1st Rev boost is always horizontal -// The 2nd Rev boost is always vertical -set g_nexball_football_physics 2 "0: Revenant's original movement, 1: 0 but half independant of aiming height, 2: 1 fully independant, -1: first recode try" -set g_nexball_basketball_bouncefactor 0.6 "velocity loss when the ball bounces" -set g_nexball_basketball_bouncestop 0.075 "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)" -set g_nexball_football_bouncefactor 0.6 "velocity loss when the ball bounces" -set g_nexball_football_bouncestop 0.075 "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)" - -set g_nexball_football_boost_forward 100 "forward velocity boost when the ball is touched" -set g_nexball_football_boost_up 200 "vertical velocity boost when the ball is touched" - -set g_nexball_basketball_delay_hold 20 "time before a player who caught the ball loses it (anti-ballcamp)" -set g_nexball_basketball_delay_hold_forteam 60 "time before a ball reset when a team holds the ball for too long" -set g_nexball_basketball_teamsteal 1 "1 to allow players to steal from teammates, 0 to disallow" - -set g_nexball_basketball_carrier_highspeed 0.8 "speed multiplier for the ballcarrier" - -set g_nexball_meter_period 1 "time to make a full cycle on the power meter" -set g_nexball_basketball_meter 1 "use the power meter for basketball" -set g_nexball_basketball_meter_minpower 0.5 "minimal multiplier to the launching speed when using the power meter" -set g_nexball_basketball_meter_maxpower 1.2 "maximal multiplier to the launching speed when using the power meter" - -set g_nexball_delay_goal 3 "delay between a goal and a ball reset" -set g_nexball_delay_idle 10 "maximal idle time before a reset" -set g_nexball_delay_start 3 "time the ball stands on its spawn before being released" -set g_nexball_delay_collect 0.5 "time before the same player can catch the ball he launched" - -set g_nexball_sound_bounce 1 "bouncing sound (0: off)" - -set g_nexball_basketball_trail 1 "1 to leave a trail" -set g_nexball_football_trail 0 "1 to leave a trail" -set g_nexball_trail_color 254 "1-256 for different colors (Quake palette, 254 is white)" - -set g_nexball_radar_showallplayers 1 "1: show every player and the ball on the radar 0: only show teammates and the ball on the radar" - set g_bloodloss 0 "amount of health below which blood loss occurs" set g_footsteps 1 "serverside footstep sounds" @@ -793,8 +498,26 @@ set g_multijump 0 "Number of multiple jumps to allow (jumping again in the air), set g_multijump_add 0 "0 = make the current z velocity equal to jumpvelocity, 1 = add jumpvelocity to the current z velocity" set g_multijump_speed -999999 "Minimum vertical speed a player must have in order to jump again" +set g_throughfloor_debug 0 "enable debugging messages for throughfloor calculations" +set g_throughfloor_damage_max_stddev 2 "Maximum standard deviation for splash damage" +set g_throughfloor_force_max_stddev 10 "Maximum standard deviation for splash force" +set g_throughfloor_min_steps_player 1 "Minimum number of steps for splash damage" +set g_throughfloor_min_steps_other 1 "Minimum number of steps for splash damage" +set g_throughfloor_max_steps_player 100 "Maximum number of steps for splash damage" +set g_throughfloor_max_steps_other 10 "Maximum number of steps for splash damage" +// note: for damage X, 0.25 * ((1-g_throughfloor_damage)*X / g_throughfloor_damage_max_stddev)^2 steps are used +// for these numbers: +// damage 25: 3 +// damage 60: 15 +// damage 80: 25 +// damage 200: 157 +// force 250: 10 +// force 300: 15 +// force 600: 57 + // effects r_glsl_vertextextureblend_usebothalphas 1 // allows to abuse texture blending as detail texture +mod_q3shader_force_terrain_alphaflag 1 // supposedly now required for r_glsl_vertextextureblend_usebothalphas to work r_glsl_postprocess 0 // but note, hud_postprocessing enables this r_picmipsprites 0 // Xonotic uses sprites that should never be picmipped (team mate, typing, waypoints) r_picmipworld 1 @@ -853,7 +576,6 @@ sv_sound_watersplash "" seta cl_announcer default "name of the announcer you wish to use from data/sound/announcer" seta cl_announcer_antispam 2 "number of seconds before an announcement of the same sound can be played again" seta cl_announcer_maptime 3 "play announcer sound telling you the remaining maptime - 0: do not play at all, 1: play at one minute, 2: play at five minutes, 3: play both" -seta cl_notify_carried_items "3" "notify you of carried items when you obtain them (e.g. flags in CTF) - 0: disabled, 1: notify of taken items, 2: notify of picking up dropped items, 3: notify of both" // startmap_dm is used when running with the -listen or -dedicated commandline options set serverconfig server.cfg @@ -1140,53 +862,9 @@ alias "g_waypointsprite_toggle" "toggle cl_hidewaypoints" // key for that? seta cl_hidewaypoints 0 "disable static waypoints, only show team waypoints" -seta g_waypointsprites_turrets 1 "disable turret waypoints" -seta g_waypointsprites_turrets_maxdist 4000 "max distace for turret sprites" - -// key hunt -set g_keyhunt 0 "Key Hunt: collect all keys from the enemies and bring them together to score" -set g_balance_keyhunt_delay_return 60 -set g_balance_keyhunt_delay_round 5 -set g_balance_keyhunt_delay_tracking 10 -set g_balance_keyhunt_delay_fadeout 2 -set g_balance_keyhunt_delay_collect 1.5 -set g_balance_keyhunt_maxdist 150 -set g_balance_keyhunt_score_collect 3 -set g_balance_keyhunt_score_carrierfrag 2 -set g_balance_keyhunt_score_capture 100 -set g_balance_keyhunt_score_push 60 -set g_balance_keyhunt_score_destroyed 50 -set g_balance_keyhunt_score_destroyed_ownfactor 1 -set g_balance_keyhunt_dropvelocity 300 -set g_balance_keyhunt_throwvelocity 400 -set g_balance_keyhunt_protecttime 0.8 -set g_balance_keyhunt_damageforcescale 1 -seta g_keyhunt_teams_override 0 -set g_keyhunt_teams 0 - -// keepaway -set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details" -set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)" -set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball" -set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score" -set g_keepaway_score_timepoints 0 "points to add to score per timeinterval, 0 for no points" -set g_keepaway_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" -set g_keepaway_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)" -set g_keepaway_ballcarrier_damage 1 "damage multiplier while holding the ball" -set g_keepaway_ballcarrier_force 1 "force multiplier while holding the ball" -set g_keepaway_ballcarrier_selfdamage 1 "self damage multiplier while holding the ball" -set g_keepaway_ballcarrier_selfforce 1 "self force multiplier while holding the ball" -set g_keepaway_noncarrier_warn 1 "warn players when they kill without holding the ball" -set g_keepaway_noncarrier_damage 1 "damage done to other players if both you and they don't have the ball" -set g_keepaway_noncarrier_force 1 "force done to other players if both you and they don't have the ball" -set g_keepaway_noncarrier_selfdamage 1 "self damage if you don't have the ball" -set g_keepaway_noncarrier_selfforce 1 "self force if you don't have the ball" -set g_keepawayball_effects 0 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" -set g_keepawayball_trail_color 254 "particle trail color from player/ball" -set g_keepawayball_damageforcescale 3 "Scale of force which is applied to the ball by weapons/explosions/etc" -set g_keepawayball_respawntime 10 "if no one picks up the ball, how long to wait until the ball respawns" -seta g_keepaway_teams_override 0 -set g_keepaway_teams 0 +seta g_waypointsprite_turrets 1 "disable turret waypoints" +seta g_waypointsprite_turrets_maxdist 5000 "max distace for turret sprites" +seta g_waypointsprite_tactical 1 "tactical overlay on turrets when in a vehicle" // so it can be stuffcmd-ed still set cl_gravity 800 "but ignored anyway" @@ -1285,6 +963,7 @@ seta scoreboard_offset_left 0.15 "how far (by percent) the scoreboard is offset seta scoreboard_offset_right 0.15 "how far (by percent) the scoreboard is offset from the right screen edge" seta scoreboard_offset_vertical 0.05 "how far (by percent) the scoreboard is offset from the top and bottom of the screen" seta scoreboard_bg_scale 0.25 "scale for the tiled scoreboard background" +seta scoreboard_respawntime_decimals 1 "decimal places to show for the respawntime countdown display on the scoreboard" seta accuracy_color_levels "0 20 100" "accuracy values at which a specified color (accuracy_color) will be used. If your accuracy is between 2 of these values then a mix of the Xth and X+1th colors will be used. You can specify up to 10 values, in increasing order" seta accuracy_color0 "1 0 0" @@ -1337,13 +1016,14 @@ set con_completion_ply *.dem set con_completion_tdem *.dem set con_completion_exec *.cfg set con_completion_chmap map +set con_completion_devmap map set con_completion_gotomap map set con_completion_vmap map set con_completion_vnextmap map set con_completion_vdomap map set con_completion_playermodel models/player/*.iqm -// these non-saved engine cvars shall be savedG +// these non-saved engine cvars shall be saved seta cl_port $cl_port seta r_showsurfaces $r_showsurfaces seta r_ambient $r_ambient @@ -1355,6 +1035,7 @@ seta vid_gl20 $vid_gl20 seta vid_gl13 $vid_gl13 seta r_drawviewmodel $r_drawviewmodel seta v_idlescale $v_idlescale +seta net_slist_queriespersecond $net_slist_queriespersecond // ticrate //sys_ticrate 0.0166667 @@ -1409,9 +1090,6 @@ set sv_maxidle 0 // when sv_maxidle is not 0, assume spectators are idle too set sv_maxidle_spectatorsareidle 0 -// CTF capture limit placeholder cvar -set capturelimit 0 - // these entities are not referenced by anything directly, they just represent // teams and are found by find() when needed prvm_leaktest_ignore_classnames "ctf_team dom_team tdm_team" @@ -1577,56 +1255,13 @@ seta cl_modeldetailreduction 1 "the higher, the less detailed certain map models set g_mapinfo_settemp_acl "+*" "ACL for mapinfo setting cvars" -// hooks -alias _cl_hook_gamestart "set _cl_hook_gametype $1; _cl_hook_gamestart_stage2" -alias _cl_hook_gamestart_stage2 "cl_hook_gamestart_all; cl_hook_gamestart_${_cl_hook_gametype}" -alias cl_hook_gamestart_all -alias cl_hook_gamestart_nop //is only called when CSQC unloads before knowing the gametype, very unlikely -alias cl_hook_gamestart_dm -alias cl_hook_gamestart_tdm -alias cl_hook_gamestart_dom -alias cl_hook_gamestart_ctf -alias cl_hook_gamestart_rune -alias cl_hook_gamestart_lms -alias cl_hook_gamestart_arena -alias cl_hook_gamestart_ca -alias cl_hook_gamestart_kh -alias cl_hook_gamestart_ons -alias cl_hook_gamestart_as -alias cl_hook_gamestart_rc -alias cl_hook_gamestart_nexball -alias cl_hook_gamestart_cts -alias cl_hook_gamestart_ka -alias cl_hook_gamestart_freezetag -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_nexball -alias sv_hook_gamestart_cts -alias sv_hook_gamestart_ka -alias sv_hook_gamestart_freezetag -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 seta cl_vehicle_spiderbot_cross_size 1 +seta cl_vehicles_hudscale 0.5 +seta cl_vehicles_hudalpha 0.75 +seta cl_vehicles_hud_tactical 1 //cl_gunalign calculator seta menu_cl_gunalign 3 "Gun alignment; 1 = center (if allowed by g_shootfromclient) or right, 2 = center (if allowed by g_shootfromclient) or left, 3 = right only, 4 = left only" @@ -1649,9 +1284,6 @@ set g_triggerimpulse_directional_multiplier 1 "trigger_impulse directional field set g_triggerimpulse_radial_multiplier 1 "trigger_impulse radial field multiplier" set the_goggles "they do nothing" "but the googles, they do" -set g_ghost_items 1 "enable ghosted items (when between 0 and 1, overrides the alpha value)" -set g_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged" - set sv_weaponstats_file "" "when set to a file name, per-weapon stats get written to that file" seta cl_noantilag 0 "turn this on if you believe antilag is bad" @@ -1677,14 +1309,14 @@ set loddebug 0 "force this LOD level" set spawn_debugview 0 "display spawnpoints and their rating on spawn to debug spawnpoint rating calculation" set g_mutatormsg "" "mutator message" set speedmeter 0 "print landing speeds" -set developer_shtest 0 "experimental speedhack detection" +set developer_csqcentities 0 "csqc entity spam" set waypoint_benchmark 0 "quit after waypoint loading to benchmark bot navigation code" set g_debug_bot_commands 0 "print scripted bot commands before executing" set g_debug_defaultsounds 0 "always use default sounds" set sv_use_csqc_players 1 "set to 0 to disable CSQC players for better Xonotic 0.5 compat" set cl_precacheplayermodels 0 "TODO please check if this needs to be 1 or if precaching a model the server already requested is fast enough to do it at runtime" seta cl_forceplayermodels 0 "make everyone look like your own model (requires server to have sv_use_csqc_players 1 and sv_defaultcharacter 0)" -seta cl_forceplayercolors 0 "make everyone look like your own color (requires server to have sv_use_csqc_players 1 and sv_defaultcharacter 0, and is ignored in teamplay)" +seta cl_forceplayercolors 0 "make everyone look like your own color (requires server to have sv_use_csqc_players 1 and sv_defaultcharacter 0, and is ignored in teamplay with more than two teams)" seta cl_forcemyplayermodel "" "set to the model file name you want to show yourself as (requires server to have sv_use_csqc_players 1; does not affect how enemies look with cl_forceplayermodels)" seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (requires server to have sv_use_csqc_players 1; does not affect how enemies look with cl_forceplayermodels)" seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (requires server to have sv_use_csqc_players 1, and is ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)" @@ -1745,7 +1377,6 @@ cl_decals_newsystem 1 set g_weaponreplace_laser "" set g_weaponreplace_shotgun "" set g_weaponreplace_uzi "" -set g_weaponreplace_minelayer "" set g_weaponreplace_grenadelauncher "" set g_weaponreplace_electro "" set g_weaponreplace_crylink "" @@ -1755,11 +1386,8 @@ set g_weaponreplace_rocketlauncher "" set g_weaponreplace_porto "" set g_weaponreplace_minstanex "" set g_weaponreplace_hook "" -set g_weaponreplace_hlac "" -set g_weaponreplace_rifle "" set g_weaponreplace_tuba "" set g_weaponreplace_fireball "" -set g_weaponreplace_seeker "" set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping uzi and shotgun (for Q3A map compatibility in mapinfo files)" set g_movement_highspeed 1 "movement speed modification factor (only changes movement when above maxspeed)" @@ -1866,8 +1494,7 @@ set g_weapon_charge_colormod_green_full -0.5 set g_weapon_charge_colormod_blue_full -1 // player statistics server URI -set g_playerstats_uri "" -set g_playerstats_debug 0 "when 1, player stats are dumped to the console too" +set g_playerstats_uri "" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory." // autoscreenshots set g_max_info_autoscreenshot 3 "how many info_autoscreenshot entities are allowed" @@ -1911,13 +1538,14 @@ scr_loadingscreen_scale_base 1 scr_loadingscreen_scale_limit 2 // other config files +exec mutator_new_toys.cfg // run BEFORE balance to make sure balance wins exec balanceXonotic.cfg -exec ctfscoring-ai.cfg exec effects-normal.cfg exec physicsX.cfg exec turrets.cfg exec vehicles.cfg exec crosshairs.cfg +exec gamemodes.cfg // load console command aliases and settings exec commands.cfg @@ -1941,3 +1569,14 @@ alias menu_sync "menu_cmd sync" set sv_join_notices "" set sv_join_notices_time 15 + +set cl_ghost_items 0.45 "enable ghosted items (when between 0 and 1, overrides the alpha value)" +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" diff --git a/effectinfo.txt b/effectinfo.txt index 05c249b976..d346a30efb 100644 --- a/effectinfo.txt +++ b/effectinfo.txt @@ -775,7 +775,7 @@ effect teleport count 500 type spark tex 64 64 -color 0xff8400 0xff2a00 +color 0x807aff 0x4463d5 size 1 1 alpha 0 256 100 stretchfactor 2 @@ -793,7 +793,7 @@ tex 65 65 size 150 150 alpha 190 190 180 sizeincrease -80 -color 0xff8400 0xff2a00 +color 0x807aff 0x4463d5 @@ -7409,4 +7409,372 @@ velocityjitter 64 64 64 //lightradius 50 //lightradiusfade 50 //lightcolor 1 0.9 0.7 -//lightshadow 1 \ No newline at end of file +//lightshadow 1 + +// heal ray muzzleflash + +effect healray_muzzleflash +countabsolute 1 +type smoke +color 0x283880 0x283880 // 0x202020 0x404040 +tex 65 65 +size 20 20 +alpha 256 256 512 +originjitter 1.5 1.5 1.5 +velocityjitter 6 6 6 +sizeincrease -10 +velocitymultiplier 0.01 +lightradius 200 +lightradiusfade 2000 +lightcolor 1.5 3 6 + +effect healray_muzzleflash +count 22 +type spark +tex 71 73 +color 0xD9FDFF 0x00f0ff +size 1 15 +sizeincrease 3 +alpha 50 150 1924 +originjitter 1 1 1 +velocityjitter 150 150 150 +velocitymultiplier 0.4 +airfriction 5 +stretchfactor 3.9 + +effect healray_muzzleflash +count 4 +type spark +tex 70 70 +color 0xD9FDFF 0x00f0ff +size 1 1 +alpha 110 228 4024 +originjitter 1 1 1 +velocityjitter 650 650 650 +velocitymultiplier 1.1 +stretchfactor 0.2 + + + +//healray impact + +effect healray_impact +countabsolute 1 +type decal +tex 59 59 +size 32 32 +alpha 256 256 0 +color 0xd800ff 0xd800ff +originjitter 17 17 17 +lightradius 125 +lightradiusfade 450 +lightcolor 0 4.375 0 +// shockwave +effect healray_impact +type smoke +countabsolute 1 +tex 33 33 +size 32 32 +sizeincrease 1400 +color 0x00ff00 0x84c52f +alpha 40 40 350 +velocitymultiplier 44 +// cloud of bouncing sparks +effect healray_impact +count 30 +type spark +tex 70 70 +color 0x00ff00 0x84c52f +size 1 2 +alpha 156 300 1024 +gravity 2 +airfriction 6 +originjitter 1 1 1 +velocityjitter 1112 1112 1112 +// inner cloud of smoke +effect healray_impact +count 15 +type smoke +color 0x00ff00 0x84c52f +tex 40 40 +size 2 3 +alpha 200 456 512 +airfriction 3 +gravity -2 +velocityjitter 120 120 420 +rotate -180 180 -90 90 + + + + +// big plasma muzzle flash + +effect bigplasma_muzzleflash +countabsolute 1 +type smoke +color 0x283880 0x283880 // 0x202020 0x404040 +tex 65 65 +size 50 50 +alpha 256 256 812 +originjitter 1.5 1.5 1.5 +velocityjitter 6 6 6 +sizeincrease -10 +velocitymultiplier 0.01 +lightradius 200 +lightradiusfade 2000 +lightcolor 1.5 3 6 + +effect bigplasma_muzzleflash +countabsolute 1 +type smoke +color 0x00f0ff 0x00f0ff +tex 74 74 +size 20 20 +alpha 56 56 1112 +sizeincrease 300 + +effect bigplasma_muzzleflash +count 14 +type spark +tex 51 55 +color 0xD9FDFF 0x00f0ff +size 5 10 +sizeincrease 135 +alpha 50 150 1924 +originjitter 1 1 1 +velocityjitter 350 350 350 +velocitymultiplier 0.4 +airfriction 5 +stretchfactor 1.9 + +effect bigplasma_muzzleflash +count 4 +type spark +tex 70 70 +color 0xD9FDFF 0x00f0ff +size 20 20 +alpha 110 228 4024 +originjitter 1 1 1 +velocityjitter 650 650 650 +velocitymultiplier 1.1 +stretchfactor 0.2 + + +// big plasma impact + +effect bigplasma_impact +countabsolute 1 +type decal +tex 59 59 +size 32 32 +alpha 256 256 0 +originjitter 17 17 17 +lightradius 125 +lightradiusfade 450 +lightcolor 3.125 4.375 10 +// shockwave +effect bigplasma_impact +type smoke +countabsolute 1 +tex 33 33 +size 32 32 +sizeincrease 1400 +color 0x80C0FF 0x80C0FF +alpha 40 40 350 +velocitymultiplier 44 +// cloud of bouncing sparks +effect bigplasma_impact +count 30 +type spark +tex 70 70 +color 0x629dff 0x0018ff +size 1 2 +alpha 156 300 1024 +gravity 2 +airfriction 6 +originjitter 1 1 1 +velocityjitter 1512 1512 1512 +// inner cloud of smoke +effect bigplasma_impact +count 15 +type smoke +color 0x629dff 0x0018ff +tex 48 55 +size 20 24 +sizeincrease 555 +alpha 200 456 1512 +airfriction 30 +originjitter 20 20 20 +velocityjitter 320 320 320 +rotate -180 180 -9 9 +// smoke +effect bigplasma_impact +type smoke +count 16 +blend alpha +tex 0 7 +size 60 30 +color 0x222222 0x000000 +alpha 128 328 390 +rotate -180 180 2 -2 +velocityjitter 100 100 200 +velocityoffset 0 0 180 +originjitter 80 80 10 +sizeincrease 30 +airfriction 0.04 +gravity 0.4 +// smoke in the middle +effect bigplasma_impact +type alphastatic +count 10 +tex 0 7 +size 60 70 +color 0x222222 0x000000 +alpha 128 328 310 +rotate -180 180 20 -20 +velocityjitter 10 10 10 +originjitter 80 80 80 +sizeincrease -10 +airfriction 0.04 +gravity -0.2 + +// redflag_touch -- effects for touching the red flag +// used nowhere in code +effect redflag_touch +count 35 +type spark +tex 40 40 +color 0xFF0000 0x970000 +size 1 3 +alpha 0 256 556 +gravity 1 +bounce 1.5 +originjitter 1 1 1 +velocityjitter 300 300 300 +velocitymultiplier 0.5 +airfriction 3 + +// blueflag_touch -- effects for touching the blue flag +// used nowhere in code +effect blueflag_touch +count 35 +type spark +tex 40 40 +color 0x0000FF 0x000097 +size 1 3 +alpha 0 256 556 +gravity 1 +bounce 1.5 +originjitter 1 1 1 +velocityjitter 300 300 300 +velocitymultiplier 0.5 +airfriction 3 + +// red_pass +// used nowhere in code +effect red_pass +trailspacing 64 +color 0xFF0000 0x970000 +size 2 2 +tex 32 32 +alpha 64 128 64 +airfriction 5 +sizeincrease 2 +type static +effect red_pass +trailspacing 12 +color 0xFF0000 0x970000 +size 1 1 +tex 0 8 +alpha 32 64 32 +airfriction 9 +sizeincrease 8 +velocityjitter 64 64 64 +type static +effect red_pass +trailspacing 12 +color 0xFF0000 0x970000 +size 4 4 +//tex 48 55 +alpha 256 256 1280 +type static + +// blue_pass +// used nowhere in code +effect blue_pass +trailspacing 64 +color 0x0000FF 0x000097 +size 2 2 +tex 32 32 +alpha 64 128 64 +airfriction 5 +sizeincrease 2 +type static +effect blue_pass +trailspacing 12 +color 0x0000FF 0x000097 +size 1 1 +tex 0 8 +alpha 32 64 32 +airfriction 9 +sizeincrease 8 +velocityjitter 64 64 64 +type static +effect blue_pass +trailspacing 12 +color 0x0000FF 0x000097 +size 4 4 +//tex 48 55 +alpha 256 256 1280 +type static + +// red_cap -- red team capture effect +effect red_cap +count 500 +type spark +tex 64 64 +color 0xFF0000 0x970000 +size 1 1 +alpha 0 256 100 +stretchfactor 2 +//gravity 1 +bounce 1.5 +originjitter 1 1 1 +velocityjitter 1000 1000 1500 +velocitymultiplier 0.5 +airfriction 2 +stretchfactor 0.6 +effect red_cap +countabsolute 1 +type smoke +tex 65 65 +size 150 150 +alpha 190 190 180 +sizeincrease -80 +color 0xFF0000 0x970000 + +// blue_cap -- blue team capture effect +effect blue_cap +count 500 +type spark +tex 64 64 +color 0x0000FF 0x000097 +size 1 1 +alpha 0 256 100 +stretchfactor 2 +//gravity 1 +bounce 1.5 +originjitter 1 1 1 +velocityjitter 1000 1000 1500 +velocitymultiplier 0.5 +airfriction 2 +stretchfactor 0.6 +effect blue_cap +countabsolute 1 +type smoke +tex 65 65 +size 150 150 +alpha 190 190 180 +sizeincrease -80 +color 0x0000FF 0x000097 + diff --git a/effects-ultra.cfg b/effects-ultra.cfg index 4c4c09baff..f92230f16f 100644 --- a/effects-ultra.cfg +++ b/effects-ultra.cfg @@ -1,5 +1,5 @@ cl_decals 1 -cl_decals_models 1 +cl_decals_models 0 cl_decals_time 10 cl_particles_quality 1 cl_damageeffect 1 diff --git a/gamemodes.cfg b/gamemodes.cfg new file mode 100644 index 0000000000..6cff7e5a42 --- /dev/null +++ b/gamemodes.cfg @@ -0,0 +1,474 @@ +// =================================== +// Master config for core game modes +// =================================== + +// global gametype setting (1 = deathmatch) +set gamecfg 1 + +// say aliases +alias asay_ctf_flagcarrier "say_team flag carrier at %y" +alias asay_ctf_haveflag "say_team (%l) have the flag" +alias asay_willgo "say_team will go to %y" +alias asay_support "say_team (%l) need help, %h%%" +alias asay_killed "say_team got killed at %d" +alias asay_noammo "say_team (%l) need %W for %w" +alias asay_drop "say_team (%l) dropped %w ; impulse 17" + + +// ================= +// gamestart hooks +// ================= +alias _cl_hook_gamestart "set _cl_hook_gametype $1; _cl_hook_gamestart_stage2" +alias _cl_hook_gamestart_stage2 "cl_hook_gamestart_all; cl_hook_gamestart_${_cl_hook_gametype}" +alias cl_hook_gamestart_all +alias cl_hook_gamestart_nop //is only called when CSQC unloads before knowing the gametype, very unlikely +alias cl_hook_gamestart_dm +alias cl_hook_gamestart_tdm +alias cl_hook_gamestart_dom +alias cl_hook_gamestart_ctf +alias cl_hook_gamestart_rune +alias cl_hook_gamestart_lms +alias cl_hook_gamestart_arena +alias cl_hook_gamestart_ca +alias cl_hook_gamestart_kh +alias cl_hook_gamestart_ons +alias cl_hook_gamestart_as +alias cl_hook_gamestart_rc +alias cl_hook_gamestart_nb +alias cl_hook_gamestart_cts +alias cl_hook_gamestart_ka +alias cl_hook_gamestart_ft +alias cl_hook_gameend +alias cl_hook_activeweapon + +alias _sv_hook_gamestart "set _sv_hook_gametype $1; _sv_hook_gamestart_stage2" +alias _sv_hook_gamestart_stage2 "sv_hook_gamestart_all; sv_hook_gamestart_${_sv_hook_gametype}" +alias sv_hook_gamestart_all +alias sv_hook_gamestart_dm +alias sv_hook_gamestart_tdm +alias sv_hook_gamestart_dom +alias sv_hook_gamestart_ctf +alias sv_hook_gamestart_rune +alias sv_hook_gamestart_lms +alias sv_hook_gamestart_arena +alias sv_hook_gamestart_ca +alias sv_hook_gamestart_kh +alias sv_hook_gamestart_ons +alias sv_hook_gamestart_as +alias sv_hook_gamestart_rc +alias sv_hook_gamestart_nb +alias sv_hook_gamestart_cts +alias sv_hook_gamestart_ka +alias sv_hook_gamestart_ft +alias sv_hook_gamerestart +alias sv_hook_gameend + + +// =========== +// leadlimit +// =========== +// this means that timelimit can be overidden globally and fraglimit can be overidden for each game mode: DM/TDM, Domination, CTF, and Runematch. +set leadlimit 0 +set leadlimit_and_fraglimit 0 "if set, leadlimit is ANDed with fraglimit (otherwise ORed)" +seta timelimit_override -1 "Time limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta fraglimit_override -1 "Frag limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta leadlimit_override -1 "Lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta capturelimit_override -1 "Capture limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta captureleadlimit_override -1 "Capture llead imit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_arena_point_limit -1 "Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_arena_point_leadlimit -1 "Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_domination_point_limit -1 "Domination point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_domination_point_leadlimit -1 "Domination point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_runematch_point_limit -1 "Runematch point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_runematch_point_leadlimit -1 "Runematch point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_keyhunt_point_limit -1 "Keyhunt point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_keyhunt_point_leadlimit -1 "Keyhunt point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_race_laps_limit -1 "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" + + +// ================================= +// respawn delay/waves/weapon_stay +// ================================= +// when variables are set to anything other than 0, they take over the global setting... +// to force disable delay or waves, set them to 0.125 +set g_ctf_respawn_delay 0 +set g_ctf_respawn_waves 0 +set g_ctf_weapon_stay 0 +set g_dm_respawn_delay 0 +set g_dm_respawn_waves 0 +set g_dm_weapon_stay 0 +set g_dom_respawn_delay 0 +set g_dom_respawn_waves 0 +set g_dom_weapon_stay 0 +set g_lms_respawn_delay 0 +set g_lms_respawn_waves 0 +set g_lms_weapon_stay 0 +set g_rune_respawn_delay 0 +set g_rune_respawn_waves 0 +set g_rune_weapon_stay 0 +set g_tdm_respawn_delay 0 +set g_tdm_respawn_waves 0 +set g_tdm_weapon_stay 0 +set g_ka_respawn_delay 0 +set g_ka_respawn_waves 0 +set g_ka_weapon_stay 0 +set g_kh_respawn_delay 0 +set g_kh_respawn_waves 0 +set g_kh_weapon_stay 0 +set g_arena_respawn_delay 0 +set g_arena_respawn_waves 0 +set g_arena_weapon_stay 0 +set g_ca_respawn_delay 0 +set g_ca_respawn_waves 0 +set g_ca_weapon_stay 0 +set g_nb_respawn_delay 0 +set g_nb_respawn_waves 0 +set g_nb_weapon_stay 0 +set g_as_respawn_delay 0 +set g_as_respawn_waves 0 +set g_as_weapon_stay 0 +set g_ons_respawn_delay 0 +set g_ons_respawn_waves 0 +set g_ons_weapon_stay 0 +set g_rc_respawn_waves 0 +set g_rc_respawn_delay 0 +set g_rc_weapon_stay 0 +set g_cts_respawn_waves 0 +set g_cts_respawn_delay 0 +set g_cts_weapon_stay 2 +set g_ft_respawn_waves 0 +set g_ft_respawn_delay 0 +set g_ft_weapon_stay 0 + + +// ======= +// arena +// ======= +set g_arena 0 "Arena: many one-on-one rounds are played to find the winner" +set g_arena_maxspawned 2 "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)" +set g_arena_roundbased 1 "if disabled, the next player will spawn as soon as someone dies" +set g_arena_warmup 5 "time, newly spawned players have to prepare themselves in round based matches" + + +// ========= +// assault +// ========= +set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend the base against the enemy for that time to win" + + +// ============ +// clan arena +// ============ +set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round." +set g_ca_point_limit 10 "point limit 10 is standard for clan arena" +set g_ca_point_leadlimit 0 +set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games." +set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts" +set g_ca_damage2score_multiplier 0.01 +set g_ca_round_timelimit 180 + + +// ================== +// capture the flag +// ================== +set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score" +set g_ctf_flag_return_time 15 +set g_ctf_flag_return_dropped 100 +set g_ctf_flag_return_damage 0 +set g_ctf_flag_return_when_unreachable 1 "automatically return the flag if it falls into lava/slime/trigger hurt" +set g_ctf_flagcarrier_auto_helpme_damage 100 "automatically place a helpme notification on flag carrier waypointsprite if they get hit and their health dips below this value" +set g_ctf_flagcarrier_auto_helpme_time 2 "antispam time for the helpme notification" +set g_ctf_flagcarrier_selfdamagefactor 1 +set g_ctf_flagcarrier_selfforcefactor 1 +set g_ctf_flagcarrier_damagefactor 1 +set g_ctf_flagcarrier_forcefactor 1 +set g_ctf_stalemate 1 "show the enemy flagcarrier location after both teams have held the flags a certain amount of time" +set g_ctf_stalemate_endcondition 1 "condition for stalemate mode to be finished: 1 = If ONE flag is no longer stale, 2 = If BOTH flags are no longer stale" +set g_ctf_stalemate_time 60 "time for each flag until stalemate mode is activated" +set g_ctf_flagcarrier_waypointforenemy_spotting 1 "show the enemy flagcarrier location if a team mate presses +use to spot them" +set g_ctf_dropped_capture_delay 1 "dropped capture delay" +set g_ctf_dropped_capture_radius 100 "allow dropped flags to be automatically captured by base flags if the dropped flag is within this radius of it" +set g_ctf_flag_damageforcescale 2 +set g_ctf_portalteleport 0 "allow flag carriers to go through portals made in portal gun without dropping the flag" +set g_ctf_reverse 0 "if enabled, flags positions are switched: you have to capture the enemy's flag from your own base by bringing it to your own flag in the enemy base" +set g_ctf_flag_collect_delay 1 +set g_ctf_flag_health 0 +set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players" +set g_ctf_flag_dropped_floatinwater 200 "move upwards while in water at this velocity" +set g_ctf_flag_pickup_verbosename 0 "show the name of the person who picked up the flag too" +set g_ctf_throw 1 "throwing allows circumventing carrierkill score, so enable this with care!" +set g_ctf_throw_angle_max 90 "maximum upwards angle you can throw the flag" +set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag" +set g_ctf_throw_punish_count 2 +set g_ctf_throw_punish_delay 30 +set g_ctf_throw_punish_time 5 +set g_ctf_throw_strengthmultiplier 2 "multiplier for velocity when you have the strength... essentially, throw the flag REALLY hard when you have the strength :D" +set g_ctf_throw_velocity_forward 500 "how fast or far a player can throw the flag" +set g_ctf_throw_velocity_up 200 "upwards velocity added upon initial throw" +set g_ctf_drop_velocity_up 200 "upwards velocity when a flag is dropped (i.e. when a flag carrier dies)" +set g_ctf_drop_velocity_side 100 "randomized sideways velocity when a flag is dropped" +set g_ctf_pass 1 "allow passing of flags to nearby team mates" +set g_ctf_pass_arc 20 "upwards arcing of the flag path to look more like a throw" +set g_ctf_pass_arc_max 200 "maximum height for upwards arcing of the flag path to look more like a throw" +set g_ctf_pass_directional_max 200 "maximum radius from crosshair for line of sight selection when passing" +set g_ctf_pass_directional_min 50 "minimum radius from crosshair for line of sight selection when passing" +set g_ctf_pass_radius 500 "maximum radius that you can pass to a team mate in" +set g_ctf_pass_wait 2 "delay in seconds between how often players can pass the flag (antispam, essentially)" +set g_ctf_pass_request 1 "allow players to request the flag carrier to pass the flag to them" +set g_ctf_pass_turnrate 50 "how well the flag follows the best direction to its target while passing" +set g_ctf_pass_timelimit 2 "how long a flag can stay trying to pass before it gives up and just becomes dropped" +set g_ctf_pass_velocity 750 "how fast or far a player can pass the flag" +set g_ctf_allow_vehicle_touch 0 "allow flags to be picked up/captured/returned without even leaving the vehicle" +set g_ctf_allow_vehicle_carry 1 "allow players to hold flags inside a vehicle" + +set g_ctf_shield_max_ratio 0 "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)" +set g_ctf_shield_min_negscore 20 "shield the player from the flag if he's got this negative amount of points or less" +set g_ctf_shield_force 100 "push force of the shield" + +set g_ctf_flag_red_model "models/ctf/flags.md3" +set g_ctf_flag_red_skin 0 +set g_ctf_flag_blue_model "models/ctf/flags.md3" +set g_ctf_flag_blue_skin 1 +set g_ctf_flag_glowtrails 1 +set g_ctf_fullbrightflags 0 +set g_ctf_dynamiclights 0 +set g_ctf_captimerecord_always 0 "always show capture time information when someone captures the flag" + +seta g_ctf_ignore_frags 0 "1: regular frags give no points" +exec ctfscoring-samual.cfg + + +// ==================== +// complete the stage +// ==================== +set g_cts 0 "CTS: complete the stage" +set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts" +set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible" + + +// ========================== +// deathmatch (ffa or team) +// ========================== +set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins" +set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)" +set g_tdm_team_spawns 0 "when 1, a map can define team spawnpoints for TDM" +seta g_tdm_teams_override 0 "how many teams are in team deathmatch" + + +// ============ +// domination +// ============ +set g_domination 0 "Domination: capture and hold control points to gain points" +set g_domination_default_teams 2 "default number of teams for maps that aren't domination-specific" +seta g_domination_teams_override 0 "use a specific number of teams in domination games (minimum 2), disables dom_team entities" +set g_domination_disable_frags 0 "players can't get frags normally, only get points from kills" +set g_domination_point_amt 0 "override: how many points to get per ping" +set g_domination_point_fullbright 0 "domination point fullbright" +set g_domination_point_rate 0 "override: how often to give those points" +set g_domination_point_capturetime 0.1 "how long it takes to capture a point (given no interference)" +set g_domination_point_glow 0 "domination point glow (warning, slow)" +//set g_domination_balance_team_points 1 "# of points received is based on team sizes" + + +// =========== +// freezetag +// =========== +set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them" +seta g_freezetag_warmup 5 "Time players get to run around before the round starts" +seta g_freezetag_point_limit -1 "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_freezetag_point_leadlimit -1 "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate" +seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range" +seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him" +seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with" + + +// ========== +// keepaway +// ========== +set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details" +set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)" +set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball" +set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score" +set g_keepaway_score_timepoints 0 "points to add to score per timeinterval, 0 for no points" +set g_keepaway_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" +set g_keepaway_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)" +set g_keepaway_ballcarrier_damage 1 "damage multiplier while holding the ball" +set g_keepaway_ballcarrier_force 1 "force multiplier while holding the ball" +set g_keepaway_ballcarrier_selfdamage 1 "self damage multiplier while holding the ball" +set g_keepaway_ballcarrier_selfforce 1 "self force multiplier while holding the ball" +set g_keepaway_noncarrier_warn 1 "warn players when they kill without holding the ball" +set g_keepaway_noncarrier_damage 1 "damage done to other players if both you and they don't have the ball" +set g_keepaway_noncarrier_force 1 "force done to other players if both you and they don't have the ball" +set g_keepaway_noncarrier_selfdamage 1 "self damage if you don't have the ball" +set g_keepaway_noncarrier_selfforce 1 "self force if you don't have the ball" +set g_keepawayball_effects 0 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" +set g_keepawayball_trail_color 254 "particle trail color from player/ball" +set g_keepawayball_damageforcescale 3 "Scale of force which is applied to the ball by weapons/explosions/etc" +set g_keepawayball_respawntime 10 "if no one picks up the ball, how long to wait until the ball respawns" +seta g_keepaway_teams_override 0 +set g_keepaway_teams 0 + + +// ========== +// key hunt +// ========== +set g_keyhunt 0 "Key Hunt: collect all keys from the enemies and bring them together to score" +set g_balance_keyhunt_delay_return 60 +set g_balance_keyhunt_delay_round 5 +set g_balance_keyhunt_delay_tracking 10 +set g_balance_keyhunt_delay_fadeout 2 +set g_balance_keyhunt_delay_collect 1.5 +set g_balance_keyhunt_maxdist 150 +set g_balance_keyhunt_score_collect 3 +set g_balance_keyhunt_score_carrierfrag 2 +set g_balance_keyhunt_score_capture 100 +set g_balance_keyhunt_score_push 60 +set g_balance_keyhunt_score_destroyed 50 +set g_balance_keyhunt_score_destroyed_ownfactor 1 +set g_balance_keyhunt_dropvelocity 300 +set g_balance_keyhunt_throwvelocity 400 +set g_balance_keyhunt_protecttime 0.8 +set g_balance_keyhunt_damageforcescale 1 +seta g_keyhunt_teams_override 0 +set g_keyhunt_teams 0 + + +// =================== +// last man standing +// =================== +set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins" +set g_lms_lives_override -1 +set g_lms_regenerate 0 +set g_lms_campcheck_interval 10 +set g_lms_campcheck_message "^1Don't camp!" +set g_lms_campcheck_damage 100 +set g_lms_campcheck_distance 1800 +set g_lms_last_join 3 "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives" +set g_lms_join_anytime 1 "if true, new players can join, but get same amount of lives as the worst player" + + +// ========= +// nexball +// ========= +set g_nexball 0 "Nexball: Basketball and Soccer go Xonotic" +set g_nexball_basketball_effects_default 8 "default: dim light. The original version used 1024 (fire) but it gives bad performance" +set g_balance_nexball_primary_speed 1000 "launching speed" +set g_balance_nexball_primary_refire 0.7 "launching refire" +set g_balance_nexball_primary_animtime 0.3 "launching animtime" +set g_balance_nexball_secondary_animtime 0.3 "launching animtime" +set g_balance_nexball_secondary_speed 3000 "stealing projectile speed" +set g_balance_nexball_secondary_lifetime 0.15 "stealing projectile lifetime" +set g_balance_nexball_secondary_force 500 "stealing projectile force" +set g_balance_nexball_secondary_refire 0.6 "stealing projectile refire" +set g_balance_nexball_secondary_animtime 0.3 "stealing projectile animtime" +set g_nexball_football_physics 2 "0: Revenant's original movement, 1: 0 but half independant of aiming height, 2: 1 fully independant, -1: first recode try" +set g_nexball_basketball_bouncefactor 0.6 "velocity loss when the ball bounces" +set g_nexball_basketball_bouncestop 0.075 "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)" +set g_nexball_football_bouncefactor 0.6 "velocity loss when the ball bounces" +set g_nexball_football_bouncestop 0.075 "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)" +set g_nexball_football_boost_forward 100 "forward velocity boost when the ball is touched" +set g_nexball_football_boost_up 200 "vertical velocity boost when the ball is touched" +set g_nexball_basketball_delay_hold 20 "time before a player who caught the ball loses it (anti-ballcamp)" +set g_nexball_basketball_delay_hold_forteam 60 "time before a ball reset when a team holds the ball for too long" +set g_nexball_basketball_teamsteal 1 "1 to allow players to steal from teammates, 0 to disallow" +set g_nexball_basketball_carrier_highspeed 0.8 "speed multiplier for the ballcarrier" +set g_nexball_meter_period 1 "time to make a full cycle on the power meter" +set g_nexball_basketball_meter 1 "use the power meter for basketball" +set g_nexball_basketball_meter_minpower 0.5 "minimal multiplier to the launching speed when using the power meter" +set g_nexball_basketball_meter_maxpower 1.2 "maximal multiplier to the launching speed when using the power meter" +set g_nexball_delay_goal 3 "delay between a goal and a ball reset" +set g_nexball_delay_idle 10 "maximal idle time before a reset" +set g_nexball_delay_start 3 "time the ball stands on its spawn before being released" +set g_nexball_delay_collect 0.5 "time before the same player can catch the ball he launched" +set g_nexball_sound_bounce 1 "bouncing sound (0: off)" +set g_nexball_basketball_trail 1 "1 to leave a trail" +set g_nexball_football_trail 0 "1 to leave a trail" +set g_nexball_trail_color 254 "1-256 for different colors (Quake palette, 254 is white)" +set g_nexball_radar_showallplayers 1 "1: show every player and the ball on the radar 0: only show teammates and the ball on the radar" +seta g_nexball_safepass_maxdist 5000 "Max distance to allow save fassping (0 to turn off safe passing)" +seta g_nexball_safepass_turnrate 0.1 "How fast the safe-pass ball can habge direction" +seta g_nexball_safepass_holdtime 0.75 "How long to remeber last teammate you pointed at" +seta g_nexball_viewmodel_scale 0.25 "How large the ball for the carrier" +seta g_nexball_viewmodel_offset "8 8 0" "Where the ball is located on carrier forward right up" +seta g_nexball_tackling 1 "Allow ball theft?" + + +// =========== +// onslaught +// =========== +set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it" +set g_onslaught_gen_health 2500 +set g_onslaught_cp_health 1000 +set g_onslaught_cp_buildhealth 100 +set g_onslaught_cp_buildtime 5 +set g_onslaught_cp_regen 20 + + +// ====== +// race +// ====== +set g_race 0 "Race: be faster than your opponents" +set g_race_qualifying_timelimit 0 +set g_race_qualifying_timelimit_override -1 +set g_race_teams 0 "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)" + + +// =========== +// runematch +// =========== +set g_runematch 0 "Runematch: pick up and hold the runes, special items that give you points, a special power (rune) and a disadvantage (curse)" +set g_runematch_pointrate 5 +set g_runematch_fixedspawns 1 "use fixed runematch spawns if available" +set g_runematch_pointamt 1 +set g_runematch_shuffletime 30 "how often runes change position" +set g_runematch_respawntime 15 "how soon after being dropped to respawn" +set g_runematch_frags_killedby_runeholder 4 +set g_runematch_frags_killed_runeholder 5 +set g_runematch_frags_norune 0 +set g_runematch_drop_runes_max 2 "only drop up to 2 runes, the rest should respawn" +set g_runematch_allow_same 0 "allow matching rune-curse pairs" +set g_runematch_rune_alpha 0.78 +set g_runematch_rune_effects 544 "EF_ADDITIVE + EF_FULLBRIGHT = 544" +set g_runematch_rune_glow_size 0 +set g_runematch_rune_glow_color 0 +set g_runematch_rune_color_strength 1.0 +// strength/weakness +set g_balance_rune_strength_damage 2.0 +set g_balance_rune_strength_force 1.5 +set g_balance_curse_weak_damage 0.5 +set g_balance_curse_weak_force 0.6 +set g_balance_rune_strength_combo_damage 0.9 +set g_balance_rune_strength_combo_force 1.0 +// defense/vulner +set g_balance_rune_defense_takedamage 0.5 +set g_balance_curse_vulner_takedamage 2.0 +set g_balance_rune_defense_combo_takedamage 1.0 +// vampire/empathy +set g_balance_rune_vampire_absorb 0.4 +set g_balance_curse_empathy_takedamage -0.4 +set g_balance_rune_vampire_combo_absorb -0.1 +set g_balance_rune_vampire_maxhealth 500 +set g_balance_curse_empathy_minhealth 20 +set g_balance_rune_vampire_combo_minhealth 40 +// regen/venom +set g_balance_rune_regen_hpmod 1.75 +set g_balance_curse_venom_hpmod 0.6 +set g_balance_rune_regen_combo_hpmod 0.9 +set g_balance_rune_regen_regenrate 3.0 +set g_balance_curse_venom_rotrate 3.0 +set g_balance_rune_regen_combo_regenrate 0.5 +set g_balance_rune_regen_combo_rotrate 1.5 +set g_balance_rune_regen_limitmod 1 +set g_balance_curse_venom_limitmod 1 +set g_balance_rune_regen_combo_limitmod 1 +// speed/slow +set g_balance_rune_speed_atkrate 0.66 +set g_balance_curse_slow_atkrate 1.5 +set g_balance_rune_speed_combo_atkrate 1.2 +set g_balance_rune_speed_highspeed 1.5 +set g_balance_curse_slow_highspeed 0.6 +set g_balance_rune_speed_combo_highspeed 0.9 + diff --git a/gfx/hud/default/key_atck.tga b/gfx/hud/default/key_atck.tga new file mode 100644 index 0000000000..40a12dc696 Binary files /dev/null and b/gfx/hud/default/key_atck.tga differ diff --git a/gfx/hud/default/key_atck2.tga b/gfx/hud/default/key_atck2.tga new file mode 100644 index 0000000000..40a12dc696 Binary files /dev/null and b/gfx/hud/default/key_atck2.tga differ diff --git a/gfx/hud/default/key_atck2_inv.tga b/gfx/hud/default/key_atck2_inv.tga new file mode 100644 index 0000000000..c1f6e42a91 Binary files /dev/null and b/gfx/hud/default/key_atck2_inv.tga differ diff --git a/gfx/hud/default/key_atck_inv.tga b/gfx/hud/default/key_atck_inv.tga new file mode 100644 index 0000000000..c1f6e42a91 Binary files /dev/null and b/gfx/hud/default/key_atck_inv.tga differ diff --git a/gfx/vehicles/axh-rings.tga b/gfx/vehicles/axh-rings.tga new file mode 100644 index 0000000000..da68ff8d90 Binary files /dev/null and b/gfx/vehicles/axh-rings.tga differ diff --git a/gfx/vehicles/bumb.tga b/gfx/vehicles/bumb.tga new file mode 100644 index 0000000000..22883f40e1 Binary files /dev/null and b/gfx/vehicles/bumb.tga differ diff --git a/gfx/vehicles/bumb_lgun.tga b/gfx/vehicles/bumb_lgun.tga new file mode 100644 index 0000000000..23db1da64e Binary files /dev/null and b/gfx/vehicles/bumb_lgun.tga differ diff --git a/gfx/vehicles/bumb_rgun.tga b/gfx/vehicles/bumb_rgun.tga new file mode 100644 index 0000000000..f755681ee0 Binary files /dev/null and b/gfx/vehicles/bumb_rgun.tga differ diff --git a/gfx/vehicles/bumb_side.tga b/gfx/vehicles/bumb_side.tga new file mode 100644 index 0000000000..69fa4d8142 Binary files /dev/null and b/gfx/vehicles/bumb_side.tga differ diff --git a/gfx/vehicles/bumb_side_gun.tga b/gfx/vehicles/bumb_side_gun.tga new file mode 100644 index 0000000000..c8fc81702d Binary files /dev/null and b/gfx/vehicles/bumb_side_gun.tga differ diff --git a/gfx/vehicles/energy.tga b/gfx/vehicles/energy.tga new file mode 100644 index 0000000000..de06ecce7a Binary files /dev/null and b/gfx/vehicles/energy.tga differ diff --git a/gfx/vehicles/vth-mover.tga b/gfx/vehicles/vth-mover.tga new file mode 100644 index 0000000000..d831896ff9 Binary files /dev/null and b/gfx/vehicles/vth-mover.tga differ diff --git a/gfx/vehicles/vth-stationary.tga b/gfx/vehicles/vth-stationary.tga new file mode 100644 index 0000000000..31703bcfea Binary files /dev/null and b/gfx/vehicles/vth-stationary.tga differ diff --git a/hud_luminos.cfg b/hud_luminos.cfg index df83636830..6472a25796 100644 --- a/hud_luminos.cfg +++ b/hud_luminos.cfg @@ -204,6 +204,7 @@ seta hud_panel_pressedkeys_bg_alpha "" seta hud_panel_pressedkeys_bg_border "" seta hud_panel_pressedkeys_bg_padding "" seta hud_panel_pressedkeys_aspect "1.600000" +seta hud_panel_pressedkeys_attack "0" seta hud_panel_chat 1 seta hud_panel_chat_pos "0.010000 0.700000" diff --git a/hud_luminos_minimal.cfg b/hud_luminos_minimal.cfg index eef7aed295..bb7e0662e3 100644 --- a/hud_luminos_minimal.cfg +++ b/hud_luminos_minimal.cfg @@ -204,6 +204,7 @@ seta hud_panel_pressedkeys_bg_alpha "" seta hud_panel_pressedkeys_bg_border "" seta hud_panel_pressedkeys_bg_padding "" seta hud_panel_pressedkeys_aspect "1.600000" +seta hud_panel_pressedkeys_attack "0" seta hud_panel_chat 1 seta hud_panel_chat_pos "0 0.775000" diff --git a/hud_luminos_minimal_xhair.cfg b/hud_luminos_minimal_xhair.cfg index 0a77fba082..e0921ad353 100644 --- a/hud_luminos_minimal_xhair.cfg +++ b/hud_luminos_minimal_xhair.cfg @@ -204,6 +204,7 @@ seta hud_panel_pressedkeys_bg_alpha "" seta hud_panel_pressedkeys_bg_border "" seta hud_panel_pressedkeys_bg_padding "" seta hud_panel_pressedkeys_aspect "1.600000" +seta hud_panel_pressedkeys_attack "0" seta hud_panel_chat 1 seta hud_panel_chat_pos "0 0.775000" diff --git a/hud_luminos_old.cfg b/hud_luminos_old.cfg index cd4b1ba711..119f7b18f6 100644 --- a/hud_luminos_old.cfg +++ b/hud_luminos_old.cfg @@ -204,6 +204,7 @@ seta hud_panel_pressedkeys_bg_alpha "" seta hud_panel_pressedkeys_bg_border "" seta hud_panel_pressedkeys_bg_padding "" seta hud_panel_pressedkeys_aspect "1.600000" +seta hud_panel_pressedkeys_attack "0" seta hud_panel_chat 1 seta hud_panel_chat_pos "0.020000 0.780000" diff --git a/hud_nexuiz.cfg b/hud_nexuiz.cfg index 67bbde531d..3cf0feb753 100644 --- a/hud_nexuiz.cfg +++ b/hud_nexuiz.cfg @@ -204,6 +204,7 @@ seta hud_panel_pressedkeys_bg_alpha "" seta hud_panel_pressedkeys_bg_border "" seta hud_panel_pressedkeys_bg_padding "" seta hud_panel_pressedkeys_aspect "1.600000" +seta hud_panel_pressedkeys_attack "0" seta hud_panel_chat 1 seta hud_panel_chat_pos "0 0.760000" diff --git a/keybinds.txt.de b/keybinds.txt.de index 1a4ca00cdd..2aac70080a 100644 --- a/keybinds.txt.de +++ b/keybinds.txt.de @@ -17,16 +17,16 @@ "weaplast" "zuletzt benutzte" "weapbest" "beste" "reload" "nachladen" -"weapon_group_1" "Laser" -"weapon_group_2" "Schrotgewehr" -"weapon_group_3" "Maschinengewehr" -"weapon_group_4" "Mortar / Mine Layer" -"weapon_group_5" "Electro" -"weapon_group_6" "Crylink / HLAC" -"weapon_group_7" "Nex / MinstaNex / Gewehr" -"weapon_group_8" "Hagar / Seeker" -"weapon_group_9" "Rocket Launcher / Fireball" -"weapon_group_0" "Port-O-Launch / Enterhaken" +"weapon_group_1" "Laser" +"weapon_group_2" "Schrotgewehr" +"weapon_group_3" "Maschinengewehr" +"weapon_group_4" "Mortar / Mine Layer" +"weapon_group_5" "Electro" +"weapon_group_6" "Crylink / HLAC" +"weapon_group_7" "Nex / Gewehr" +"weapon_group_8" "Hagar / Seeker" +"weapon_group_9" "Rocket Launcher / Fireball" +"weapon_group_0" "Port-O-Launch / Enterhaken" "" "" "" "Anzeige" "+zoom" "Vergrößern (festhalten)" diff --git a/keybinds.txt.es b/keybinds.txt.es index 3e3102c795..55c82cd3f7 100644 --- a/keybinds.txt.es +++ b/keybinds.txt.es @@ -17,16 +17,16 @@ "weaplast" "anteriormente usado" "weapbest" "mejor" "reload" "recargar" -"weapon_group_1" "Laser" -"weapon_group_2" "Shotgun" -"weapon_group_3" "Machine Gun" -"weapon_group_4" "Mortar / Mine Layer" -"weapon_group_5" "Electro" -"weapon_group_6" "Crylink / HLAC" -"weapon_group_7" "Nex / MinstaNex / Rifle" -"weapon_group_8" "Hagar / Seeker" -"weapon_group_9" "Rocket Launcher / Fireball" -"weapon_group_0" "Port-O-Launch / Hook" +"weapon_group_1" "Laser" +"weapon_group_2" "Shotgun" +"weapon_group_3" "Machine Gun" +"weapon_group_4" "Mortar / Mine Layer" +"weapon_group_5" "Electro" +"weapon_group_6" "Crylink / HLAC" +"weapon_group_7" "Nex / Rifle" +"weapon_group_8" "Hagar / Seeker" +"weapon_group_9" "Rocket Launcher / Fireball" +"weapon_group_0" "Port-O-Launch / Hook" "" "" "" "Vista" "+zoom" "mantener zoom" diff --git a/keybinds.txt.hu b/keybinds.txt.hu index da4644a9b0..74d96470be 100644 --- a/keybinds.txt.hu +++ b/keybinds.txt.hu @@ -23,7 +23,7 @@ "weapon_group_4" "Mortar / Aknavető" "weapon_group_5" "Electro" "weapon_group_6" "Crylink / HLAC" -"weapon_group_7" "Nex / MinstaNex / Puska" +"weapon_group_7" "Nex / Puska" "weapon_group_8" "Hagar / Seeker" "weapon_group_9" "Rakétavető / Tűzlabda" "weapon_group_0" "Port-O-Launch / Kampó" diff --git a/keybinds.txt.uk b/keybinds.txt.uk index 07edd86683..02b4e579b2 100644 --- a/keybinds.txt.uk +++ b/keybinds.txt.uk @@ -23,7 +23,7 @@ "weapon_group_4" "Мортира / Міноукладчик" "weapon_group_5" "Електро" "weapon_group_6" "Крайлінк / ВЛШГ" -"weapon_group_7" "Некс / МінстаНекс / Гвинтівка" +"weapon_group_7" "Некс / Гвинтівка" "weapon_group_8" "Хейгар / Шукач" "weapon_group_9" "Ракетна гармата / Метеор" "weapon_group_0" "Портал-О-Пуск / Гак" diff --git a/menu.dat.uk.po b/menu.dat.uk.po index d0504267d1..def9974b1f 100644 --- a/menu.dat.uk.po +++ b/menu.dat.uk.po @@ -6,10 +6,10 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: 0.5\n" +"Project-Id-Version: 0.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-01-26 12:24+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: 2012-03-28 19:46+0200\n" "Last-Translator: Harmata \n" "Language-Team: LANGUAGE \n" "Language: uk\n" @@ -220,19 +220,19 @@ msgstr "Мова тексту:" #: qcsrc/menu/xonotic/dialog_firstrun.c:77 msgid "Allow player statistics to use your nickname at stats.xonotic.org?" -msgstr "Äîçâîëèòè âèêîðèñòàííÿ âàøîãî ïñåâäîí³ìó íà stats.xonotic.org?" +msgstr "Дозволити використання імені гравця в статистиці на stats.xonotic.org?" #: qcsrc/menu/xonotic/dialog_firstrun.c:81 msgid "ALWU2N^Yes" -msgstr "ALWU2N^Òàê" +msgstr "ALWU2N^Так" #: qcsrc/menu/xonotic/dialog_firstrun.c:82 msgid "ALWU2N^No" -msgstr "ALWU2N^ͳ" +msgstr "ALWU2N^Ні" #: qcsrc/menu/xonotic/dialog_firstrun.c:83 msgid "ALWU2N^Undecided" -msgstr "ALWU2N^Íåâèð³øåíî" +msgstr "ALWU2N^Не вирішено" #: qcsrc/menu/xonotic/dialog_firstrun.c:87 msgid "Save settings" @@ -278,7 +278,7 @@ msgstr "Основні повідомлення" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.c:23 msgid "Message duration:" -msgstr "Тривалість існування повідомлень:" +msgstr "Тривалість життя повідомлень:" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.c:27 msgid "Fade time:" @@ -920,7 +920,7 @@ msgstr "Мутатори..." #: qcsrc/menu/xonotic/dialog_multiplayer_create.c:96 #: qcsrc/menu/xonotic/dialog_settings_misc.c:145 msgid "Advanced settings..." -msgstr "Додадкові налаштування:" +msgstr "Розширені налаштування:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.c:103 msgid "Map list:" @@ -960,7 +960,7 @@ msgstr "Ліміт фрагів:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c:6 msgid "Advanced server settings" -msgstr "Додаткові налаштування сервера" +msgstr "Розширені налаштування сервера" #: qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c:25 msgid "Game settings:" @@ -1123,7 +1123,7 @@ msgstr "Політ за допомогою ракет" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c:76 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c:210 msgid "Invincible Projectiles" -msgstr "Íåâðàçëèâ³ ñíàðÿäè" +msgstr "Невразливі снаряди" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c:80 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c:272 @@ -1176,7 +1176,7 @@ msgstr "Реактивний ранець" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c:100 msgid "No powerups" -msgstr "Áåç ï³äñèëåíü" +msgstr "Без підсилень" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c:102 msgid "Powerups" @@ -1311,11 +1311,11 @@ msgstr "Офіційні налаштування" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c:208 msgid "N/A (auth library missing, can't connect)" -msgstr "Н/Д (аутентична бібліотека відсутня, неможливо з'єднатися)" +msgstr "Н/Д (автентична бібліотека відсутня, неможливо з'єднатися)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c:210 msgid "N/A (auth library missing)" -msgstr "Í/Ä (àóòåíòè÷íà á³áë³îòåêà â³äñóòíÿ)" +msgstr "Н/Д (відсутня автентична бібліотека)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c:216 msgid "Not supported (can't connect)" @@ -1479,19 +1479,19 @@ msgstr "Налаштування прицілу" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:28 msgid "Enable center crosshair dot" -msgstr "Увімкнути крапку в центрі прицілу" +msgstr "Увімкнути цятку в центрі прицілу" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:31 msgid "Dot size:" -msgstr "Розмір крапки:" +msgstr "Розмір цятки:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:36 msgid "Dot alpha:" -msgstr "Прозорість крапки:" +msgstr "Прозорість цятки:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:41 msgid "Dot color:" -msgstr "Колір крапки:" +msgstr "Колір цятки:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:43 msgid "Use normal crosshair color" @@ -1503,11 +1503,11 @@ msgstr "Рухи прицілу:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:57 msgid "Smooth effects of crosshairs" -msgstr "Çãëàäæóâàòè åôåêòè ïðèö³ë³â" +msgstr "Згладжувати ефекти прицілу" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:61 msgid "Use rings to indicate weapon status" -msgstr "Âèêîðèñòîâóâàòè ê³ëüöÿ äëÿ ïîçíà÷åííÿ ñòàòóñó çáðî¿" +msgstr "Показувати статус зброї кільцями" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:67 msgid "Hit testing:" @@ -1527,27 +1527,27 @@ msgstr "HTTST^Вороги" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:76 msgid "Blur crosshair if the shot is obstructed" -msgstr "Ðîçìèâàòè ïðèö³ë ÿêùî ùîñü ïåðåøêîäæຠïîñòð³ëó" +msgstr "Робити приціл нечітким, якщо пострілу щось заважає" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:81 msgid "Animate when hitting an enemy" -msgstr "Ñèãíàë³çóâàòè ïðèö³ëîì êîëè âè âëó÷àºòå ó âîðîãà" +msgstr "Сигналізувати влучання прицілом" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_crosshair.c:85 msgid "Animate when picking up an item" -msgstr "Ñèãíàë³çóâàòè ïðèö³ëîì âçÿòòÿ ïðåäìåòà" +msgstr "Сигналізувати прицілом підняття предмету" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:40 msgid "Damage:" -msgstr "Шкода:" +msgstr "Затьмарення від болю:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:43 msgid "Overlay:" -msgstr "Ïîêðèòòÿ:" +msgstr "Ефект:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:47 msgid "Factor:" -msgstr "Сила зуму:" +msgstr "Сила:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:52 msgid "Fade rate:" @@ -1559,7 +1559,7 @@ msgstr "Дороговкази" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:71 msgid "Edge offset:" -msgstr "Îôñåò êðàþ:" +msgstr "Офсет краю:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:80 msgid "Show names above players" @@ -1567,7 +1567,7 @@ msgstr "Показувати імена над гравцями" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:83 msgid "Only when near crosshair" -msgstr "Ò³ëüêè êîëè ïîðó÷ ç ïðèö³ëîì" +msgstr "Тільки коли поруч з прицілом" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hud.c:86 msgid "Display health and armor" @@ -1580,19 +1580,19 @@ msgstr "Редактор HUD" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hudconfirm.c:28 msgid "In order for the HUD editor to show, you must first be in game." -msgstr "Äëÿ ðîáîòè â ðåäàêòîð³ HUD íåîáõ³äíî óâ³éòè â ãðó" +msgstr "Редактор HUD доступний лише у грі." #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hudconfirm.c:30 msgid "Do you wish to start a local game to set up the HUD?" -msgstr "Áàæàºòå ïî÷àòè ëîêàëüíó ãðó äëÿ íàëàøòóâàííÿ HUD?" +msgstr "Бажаєте почати локальну гру для налаштування HUD?" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hudconfirm.c:33 msgid "HDCNFRM^Yes" -msgstr "HDCNFRM^Òàê" +msgstr "HDCNFRM^Так" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_hudconfirm.c:36 msgid "HDCNFRM^No" -msgstr "HDCNFRM^ͳ" +msgstr "HDCNFRM^Ні" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_model.c:28 msgid "Body fading:" @@ -1644,7 +1644,7 @@ msgstr "Використовувати власну модель для всіх #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_model.c:52 msgid "Force player colors to mine" -msgstr "Âèêîðèñòîâóâàòè êîëüîðè âëàñíî¿ ìîäåë³ äëÿ âñ³õ ãðàâö³â" +msgstr "Використовувати кольори вашої моделі для усіх інших моделей" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:28 msgid "Field of view:" @@ -1660,11 +1660,11 @@ msgstr "RETICLE^На повний екран" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:34 msgid "RETICLE^With reticle" -msgstr "RETICLE^Ç ñ³òêîþ" +msgstr "RETICLE^З візирними нитками" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:38 msgid "ZOOM^Factor:" -msgstr "ZOOM^Ñèëà:" +msgstr "ZOOM^Сила:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:42 msgid "ZOOM^Speed:" @@ -1672,7 +1672,7 @@ msgstr "ZOOM^Швидкість:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:52 msgid "ZOOM^Instant" -msgstr "ZOOM^Ìèòòºâî" +msgstr "ZOOM^Миттєвий" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:56 msgid "ZOOM^Sensitivity:" @@ -1680,7 +1680,7 @@ msgstr "ZOOM^Чутливість:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:59 msgid "Velocity zoom:" -msgstr "Øâèäê³ñíèé çóì:" +msgstr "Швидкісний зум:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:61 msgid "VZOOM^Disabled" @@ -1688,43 +1688,43 @@ msgstr "VZOOM^Вимкнуто" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:62 msgid "VZOOM^Forward only" -msgstr "VZOOM^Ò³ëüêè âïåðåä" +msgstr "VZOOM^Тільки вперед" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:63 msgid "VZOOM^All directions" -msgstr "VZOOM^Âñ³ íàïðÿìè" +msgstr "VZOOM^Всі напрямки" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:67 msgid "VZOOM^Speed" -msgstr "VZOOM^Øâèäê³ñòü" +msgstr "VZOOM^Швидкість" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:72 msgid "Allow passing through walls while spectating" -msgstr "Äîçâîëèòè ïîë³ò êð³çü ñò³íè ï³ä ÷àñ ñïîñòåð³ãàííÿ" +msgstr "Дозволити рух крізь стіни підчас спостерігання" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:75 msgid "1st person perspective" -msgstr "Âèä â³ä ïåðøî¿ îñîáè" +msgstr "Вид від першої особи" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:79 msgid "Smooth the view when landing from a jump" -msgstr "Âèð³âíþâàòè îãëÿä ï³ñëÿ ïðèçåìëåííÿ" +msgstr "Вирівнювати поле зору після приземлення після стрибка" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:83 msgid "Smooth the view while crouching" -msgstr "Âèð³âíþâàòè îãëÿä ïðè ïðèñ³äàíí³" +msgstr "Вирівнювати поле зору після присідання" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:87 msgid "View waving while idle" -msgstr "Êîëèõàííÿ îãëÿäó ï³ä÷àñ áåçä³ÿëüíîñò³" +msgstr "Гойдання поля зору під час бездіяльності" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:91 msgid "View bobbing while walking around" -msgstr "Ãîéäàííÿ îãëÿäó ï³ä÷àñ õîäüáè" +msgstr "Гойдання поля зору підчас ходьби" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:96 msgid "3rd person perspective" -msgstr "Âèä â³ä òðåòüî¿ îñîáè" +msgstr "Вид від третьої особи" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_view.c:100 msgid "Back distance" @@ -1736,7 +1736,7 @@ msgstr "Відстань зверху" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_weapons.c:29 msgid "Weapon priority list:" -msgstr "Список пріоритетів зброї:" +msgstr "Список пріоритетності зброї:" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_weapons.c:33 msgid "Up" @@ -1760,7 +1760,7 @@ msgstr "Показувати модель зброї у першій особі" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_weapons.c:58 msgid "Gun model swaying" -msgstr "Êîëèõàííÿ ìîäåë³ çáðî¿" +msgstr "Гойдання моделі зброї" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_weapons.c:63 msgid "Gun model bobbing" @@ -1768,7 +1768,7 @@ msgstr "Хитання моделі зброї" #: qcsrc/menu/xonotic/dialog_multiplayer_playersetup_weapons.c:68 msgid "VWMDL^Scale" -msgstr "VWMDL^Ìàñøòàá" +msgstr "VWMDL^Розмір" #: qcsrc/menu/xonotic/dialog_news.c:4 msgid "News" @@ -1796,11 +1796,11 @@ msgstr "Ні" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:4 msgid "Sandbox Tools" -msgstr "²íñòðóìåíòè ï³ñî÷íèö³" +msgstr "Інструменти Пісочниці" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:24 msgid "Spawn" -msgstr "Ñòâîðèòè" +msgstr "Створити" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:25 msgid "Remove *" @@ -1808,7 +1808,7 @@ msgstr "Прибрати *" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:27 msgid "Copy *" -msgstr "Êîï³þâàòè *" +msgstr "Скопіювати *" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:28 msgid "Paste" @@ -1816,27 +1816,27 @@ msgstr "Вставити" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:30 msgid "Bone:" -msgstr "ʳñòêà:" +msgstr "Кістка:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:35 msgid "Set * as child" -msgstr "Âñòàíîâèòè * ÿê äèòÿ" +msgstr "Зробити * дитям" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:36 msgid "Attach to *" -msgstr "Ïðèêð³ïèòè äî *" +msgstr "Прикріпити до *" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:38 msgid "Detach from *" -msgstr "³äîêðåìèòè â³ä *" +msgstr "Відокремити від *" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:41 msgid "Visual object properties for *:" -msgstr "³çóàëüí³ âëàñòèâîñò³ îáºêòà äëÿ *:" +msgstr "Візуальні властивості об'єкту для *:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:43 msgid "Set skin:" -msgstr "Встановити скін:" +msgstr "Встановити скин:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:45 msgid "Set alpha:" @@ -1852,15 +1852,15 @@ msgstr "Встановити колір свічення:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:54 msgid "Set frame:" -msgstr "Âñòàíîâèòè êàäð:" +msgstr "Встановити кадр:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:58 msgid "Physical object properties for *:" -msgstr "Ô³çè÷í³ âëàñòèâîñò³ îáºêòà äëÿ *:" +msgstr "Фізичні властивості об'єкта для *:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:60 msgid "Set material:" -msgstr "Âñòàíîâèòè ìàòåð³àë:" +msgstr "Встановити матеріал:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:66 msgid "Set solidity:" @@ -1868,23 +1868,23 @@ msgstr "Встановити твердість:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:67 msgid "Non-solid" -msgstr "Íå òâåðäå" +msgstr "Нетверде" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:68 msgid "Solid" -msgstr "Òâåðäå" +msgstr "Тверде" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:69 msgid "Set physics:" -msgstr "Âñòàíîâèòè ô³çè÷íèé ñòàí:" +msgstr "Встановити фізику:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:70 msgid "Static" -msgstr "Íåðóõîìå" +msgstr "Нерухоме" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:71 msgid "Movable" -msgstr "Ðóõîìå" +msgstr "Рухоме" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:72 msgid "Physical" @@ -1896,23 +1896,23 @@ msgstr "Встановити масштаб:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:76 msgid "Set force:" -msgstr "Âñòàíîâèòè ñèëó:" +msgstr "Встановити силу:" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:80 msgid "Claim *" -msgstr "Âèìàãàòè *" +msgstr "Взяти *" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:82 msgid "* object info" -msgstr "* ³íôî ïðî îá'ºêò" +msgstr "* інформація об'єкта" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:83 msgid "* mesh info" -msgstr "* ³íôî ïðî ìåø" +msgstr "* інформація меша" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:84 msgid "* attachment info" -msgstr "* ³íôî ïðî ïðèêð³ïëåííÿ" +msgstr "* інформація прикріплення" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:85 msgid "Show help" @@ -1920,7 +1920,7 @@ msgstr "Показувати допомогу" #: qcsrc/menu/xonotic/dialog_sandboxtools.c:86 msgid "* is the object you are facing" -msgstr "* öå îá'ºêò ïåðåä âàìè" +msgstr "* це об'єкт перед вами" #: qcsrc/menu/xonotic/dialog_settings.c:4 msgid "Settings" @@ -1949,7 +1949,7 @@ msgstr "Звук" #: qcsrc/menu/xonotic/dialog_settings.c:22 #: qcsrc/menu/xonotic/dialog_settings_user.c:4 msgid "User" -msgstr "Êîðèñòóâà÷" +msgstr "Користувач" #: qcsrc/menu/xonotic/dialog_settings.c:23 #: qcsrc/menu/xonotic/dialog_settings_misc.c:4 @@ -1998,11 +1998,11 @@ msgstr "Зброя:" #: qcsrc/menu/xonotic/dialog_settings_audio.c:117 msgid "New style sound attenuation" -msgstr "Íîâèé ñòèëü çàòóõàííÿ çâóêó" +msgstr "Новий стиль затухання звуку" #: qcsrc/menu/xonotic/dialog_settings_audio.c:119 msgid "Mute sounds when not active" -msgstr "Ïðèáèðàòè çâóêè êîëè íå àêòèâíèé" +msgstr "Прибирати звуки підчас бездіяльності" #: qcsrc/menu/xonotic/dialog_settings_audio.c:122 #: qcsrc/menu/xonotic/dialog_settings_audio.c:194 @@ -2123,7 +2123,7 @@ msgstr "Автоматичні глузування" #: qcsrc/menu/xonotic/dialog_settings_audio.c:199 msgid "Debug info about sounds" -msgstr "Ïåðåâ³ðÿòè ³íôîðìàö³þ ïðî çâóêè íà ïîìèëêè" +msgstr "Перевіряти на помилки інформацію про звук" #: qcsrc/menu/xonotic/dialog_settings_effects.c:38 msgid "Quality preset:" @@ -2248,11 +2248,11 @@ msgstr "Ефекти шкоди:" #: qcsrc/menu/xonotic/dialog_settings_effects.c:108 msgid "DMGPRTCLS^Disabled" -msgstr "DMGPRTCLS^Вимкнуто" +msgstr "DMGPRTCLS^Вимкнуті" #: qcsrc/menu/xonotic/dialog_settings_effects.c:109 msgid "DMGPRTCLS^Skeletal" -msgstr "DMGPRTCLS^Ñêåëåòí³" +msgstr "DMGPRTCLS^Скелетні" #: qcsrc/menu/xonotic/dialog_settings_effects.c:110 msgid "DMGPRTCLS^All" @@ -2353,7 +2353,7 @@ msgstr "Ефект motion blur:" #: qcsrc/menu/xonotic/dialog_settings_effects.c:179 msgid "Extra postprocessing effects" -msgstr "Äîäàòêîâ³ åôåêòè ï³ñëÿîáðîáêè" +msgstr "Додаткові ефекти пост-обробки" #: qcsrc/menu/xonotic/dialog_settings_input.c:26 msgid "Key bindings:" @@ -2373,7 +2373,7 @@ msgstr "Кнопка \"увійти в консоль\" також закрив #: qcsrc/menu/xonotic/dialog_settings_input.c:47 msgid "Automatically repeat jumping if holding jump" -msgstr "Àâòîìàòè÷íî ïîâòîðþâàòè ñòðèáîê ÿêùî óòðèìóºòüñÿ êíîïêà ñòðèáêà" +msgstr "Автоматично продовжувати стрибати, якщо натиснута клавіша стрибок" #: qcsrc/menu/xonotic/dialog_settings_input.c:51 #: qcsrc/menu/xonotic/dialog_settings_input.c:53 @@ -2383,7 +2383,7 @@ msgstr "Використовувати джойстик" #: qcsrc/menu/xonotic/dialog_settings_input.c:61 msgid "Mouse:" -msgstr "Ìèøêà:" +msgstr "Миша:" #: qcsrc/menu/xonotic/dialog_settings_input.c:64 msgid "Sensitivity:" @@ -2391,7 +2391,7 @@ msgstr "Чутливість:" #: qcsrc/menu/xonotic/dialog_settings_input.c:68 msgid "Smooth aiming" -msgstr "Çãëàäæåíå ïðèö³ëþâàííÿ" +msgstr "Згладжувати прицілювання" #: qcsrc/menu/xonotic/dialog_settings_input.c:71 msgid "Invert aiming" @@ -2457,7 +2457,7 @@ msgstr "Вхідні пакети:" #: qcsrc/menu/xonotic/dialog_settings_misc.c:45 msgid "Local latency:" -msgstr "Ëîêàëüíà çàòðèìêà:" +msgstr "Локальна затримка:" #: qcsrc/menu/xonotic/dialog_settings_misc.c:50 msgid "Client UDP port:" @@ -2473,7 +2473,7 @@ msgstr "Передбачення руху зі сторони клієнта" #: qcsrc/menu/xonotic/dialog_settings_misc.c:62 msgid "Movement error compensation" -msgstr "Êîìïåíñàö³ÿ ïîìèëîê ðóõó" +msgstr "Компенсація помилок руху" #: qcsrc/menu/xonotic/dialog_settings_misc.c:66 msgid "Downloads:" @@ -2482,7 +2482,7 @@ msgstr "Завантажень:" #: qcsrc/menu/xonotic/dialog_settings_misc.c:69 #: qcsrc/menu/xonotic/dialog_settings_misc.c:81 msgid "Maximum:" -msgstr "Ìàêñèìóì:" +msgstr "Максимум:" #: qcsrc/menu/xonotic/dialog_settings_misc.c:73 msgid "Speed (kB/s):" @@ -2490,7 +2490,7 @@ msgstr "Швидкість (кб/с):" #: qcsrc/menu/xonotic/dialog_settings_misc.c:78 msgid "Framerate:" -msgstr "Êàäð³â â ñåêóíäó:" +msgstr "Кадри за секунду:" #: qcsrc/menu/xonotic/dialog_settings_misc.c:83 msgid "MAXFPS^5 fps" @@ -2538,11 +2538,11 @@ msgstr "MAXFPS^200 fps" #: qcsrc/menu/xonotic/dialog_settings_misc.c:94 msgid "MAXFPS^Unlimited" -msgstr "MAXFPS^Íåîáìåæåíî" +msgstr "MAXFPS^Необмежено" #: qcsrc/menu/xonotic/dialog_settings_misc.c:98 msgid "Target:" -msgstr "Ö³ëü:" +msgstr "Ціль:" #: qcsrc/menu/xonotic/dialog_settings_misc.c:100 msgid "TRGT^Disabled" @@ -2598,7 +2598,7 @@ msgstr "IDLFPS^60 fps" #: qcsrc/menu/xonotic/dialog_settings_misc.c:117 msgid "IDLFPS^Unlimited" -msgstr "IDLFPS^Íåîáìåæåíî" +msgstr "IDLFPS^Необмежено" #: qcsrc/menu/xonotic/dialog_settings_misc.c:122 msgid "Show frames per second" @@ -2606,7 +2606,7 @@ msgstr "Показувати кількість кадрів в секунду" #: qcsrc/menu/xonotic/dialog_settings_misc.c:125 msgid "Save processing time for other apps" -msgstr "Çáåð³ãàòè ïðîöåñîðíèé ÷àñ äëÿ ³íøèõ ïðîãðàì" +msgstr "Зберігати процесорний час для інших програм" #: qcsrc/menu/xonotic/dialog_settings_misc.c:129 msgid "Menu tooltips:" @@ -2618,7 +2618,7 @@ msgstr "TLTIP^Вимкнуті" #: qcsrc/menu/xonotic/dialog_settings_misc.c:132 msgid "TLTIP^Standard" -msgstr "TLTIP^Ñòàíäàðòíî" +msgstr "TLTIP^Стандартно" #: qcsrc/menu/xonotic/dialog_settings_misc.c:133 msgid "TLTIP^Advanced" @@ -2666,11 +2666,11 @@ msgstr "Вигляд меню:" #: qcsrc/menu/xonotic/dialog_settings_user.c:31 msgid "Set skin" -msgstr "Встановити скін:" +msgstr "Встановити скин" #: qcsrc/menu/xonotic/dialog_settings_user.c:74 msgid "Set language" -msgstr "Обрати мову:" +msgstr "Змінити мову" #: qcsrc/menu/xonotic/dialog_settings_user.c:79 msgid "Disable gore effects and harsh language" @@ -2678,11 +2678,11 @@ msgstr "Вимкнути криваві ефекти та нецензурні #: qcsrc/menu/xonotic/dialog_settings_user.c:82 msgid "Allow player statistics to track your client" -msgstr "Äîçâîëèòè çá³ð ñòàòèñòèêó âàøîãî ê볺íòà" +msgstr "Дозволити збір статистика про вашого гравця" #: qcsrc/menu/xonotic/dialog_settings_user.c:84 msgid "Allow player statistics to use your nickname" -msgstr "Äîçâîëèòè âèêîðèñòàííÿ âàøîãî ïñåâäîí³ìà ó ñòàòèñòèö³" +msgstr "Дозволити використання вашого нікнейму в статистиці" #: qcsrc/menu/xonotic/dialog_settings_video.c:26 msgid "Resolution:" @@ -2784,7 +2784,7 @@ msgstr "AA^Вимкнуто" #: qcsrc/menu/xonotic/dialog_settings_video.c:71 msgid "High-quality frame buffer" -msgstr "Âèñîêîÿê³ñíèé áóôåð êàäð³â" +msgstr "Високоякісний буфер кадрів" #: qcsrc/menu/xonotic/dialog_settings_video.c:76 msgid "Depth first:" @@ -2864,11 +2864,11 @@ msgstr "Чекати поки GPU закінчить кожний кадр" #: qcsrc/menu/xonotic/dialog_settings_video.c:132 msgid "Psycho coloring (easter egg)" -msgstr "Ïñèõî-êîëüîðè (âåëèêîäíº ÿéöå)" +msgstr "Психо-кольори (великоднє яйце)" #: qcsrc/menu/xonotic/dialog_settings_video.c:135 msgid "Trippy vertices (easter egg)" -msgstr "Íåíîðìàëüí³ âåðøèíè (âåëèêîäíº ÿéöå)" +msgstr "Божевільні вершини (великоднє яйце)" #: qcsrc/menu/xonotic/dialog_settings_video.c:138 msgid "Flip view horizontally" @@ -2884,19 +2884,19 @@ msgstr "Миттєвий бій! (випадкова мапа з ботами)" #: qcsrc/menu/xonotic/dialog_singleplayer.c:137 msgid "Campaign Difficulty:" -msgstr "Âàæê³ñòü êàìïàí³¿:" +msgstr "Важкість кампанії:" #: qcsrc/menu/xonotic/dialog_singleplayer.c:138 msgid "CSKL^Easy" -msgstr "CSKL^Ëåãêà" +msgstr "CSKL^Легка" #: qcsrc/menu/xonotic/dialog_singleplayer.c:139 msgid "CSKL^Medium" -msgstr "CSKL^Середній" +msgstr "CSKL^Середня" #: qcsrc/menu/xonotic/dialog_singleplayer.c:140 msgid "CSKL^Hard" -msgstr "CSKL^Âàæêà" +msgstr "CSKL^Важка" #: qcsrc/menu/xonotic/dialog_singleplayer.c:142 msgid "Start Singleplayer!" @@ -2942,7 +2942,7 @@ msgstr "Більше не натискайте цю клавішу!" msgid "" "Huh? Can't play this (m is NULL). Refiltering so this won't happen again.\n" msgstr "" -"Шо? В це неможливо грати (m is NULL). Зроблю фільтрацію ще раз, щоб такого " +"Шо? Це неможливо зіграти (m is NULL). Зроблю фільтрацію ще раз, щоб такого " "більше не траплялось.\n" #: qcsrc/menu/xonotic/maplist.c:290 @@ -3061,7 +3061,7 @@ msgstr "^1%s ТЕСТОВА ЗБІРКА" #: qcsrc/menu/xonotic/util.qc:439 #, c-format msgid "Update to %s now!" -msgstr "Негайно обновіть версію до %s !" +msgstr "Негайно оновіть версію до %s !" #: qcsrc/menu/xonotic/util.qc:524 msgid "" diff --git a/models/items/a_bullets_simple.iqm b/models/items/a_bullets_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/a_bullets_simple.iqm differ diff --git a/models/items/a_bullets_simple.iqm_0.skin b/models/items/a_bullets_simple.iqm_0.skin new file mode 100644 index 0000000000..4757695451 --- /dev/null +++ b/models/items/a_bullets_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,a_bullets_simple \ No newline at end of file diff --git a/models/items/a_bullets_simple.tga b/models/items/a_bullets_simple.tga new file mode 100644 index 0000000000..111a32a7bc Binary files /dev/null and b/models/items/a_bullets_simple.tga differ diff --git a/models/items/a_cells_simple.iqm b/models/items/a_cells_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/a_cells_simple.iqm differ diff --git a/models/items/a_cells_simple.iqm_0.skin b/models/items/a_cells_simple.iqm_0.skin new file mode 100644 index 0000000000..1651991fd6 --- /dev/null +++ b/models/items/a_cells_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,a_cells_simple \ No newline at end of file diff --git a/models/items/a_cells_simple.tga b/models/items/a_cells_simple.tga new file mode 100644 index 0000000000..0f7f1df960 Binary files /dev/null and b/models/items/a_cells_simple.tga differ diff --git a/models/items/a_rockets_simple.iqm b/models/items/a_rockets_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/a_rockets_simple.iqm differ diff --git a/models/items/a_rockets_simple.iqm_0.skin b/models/items/a_rockets_simple.iqm_0.skin new file mode 100644 index 0000000000..5b2333cc7f --- /dev/null +++ b/models/items/a_rockets_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,a_rockets_simple \ No newline at end of file diff --git a/models/items/a_rockets_simple.tga b/models/items/a_rockets_simple.tga new file mode 100644 index 0000000000..3d8646be16 Binary files /dev/null and b/models/items/a_rockets_simple.tga differ diff --git a/models/items/a_shells_simple.iqm b/models/items/a_shells_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/a_shells_simple.iqm differ diff --git a/models/items/a_shells_simple.iqm_0.skin b/models/items/a_shells_simple.iqm_0.skin new file mode 100644 index 0000000000..e3dc44f1fe --- /dev/null +++ b/models/items/a_shells_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,a_shells_simple \ No newline at end of file diff --git a/models/items/a_shells_simple.tga b/models/items/a_shells_simple.tga new file mode 100644 index 0000000000..a357f50902 Binary files /dev/null and b/models/items/a_shells_simple.tga differ diff --git a/models/items/g_h100_simple.iqm b/models/items/g_h100_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/g_h100_simple.iqm differ diff --git a/models/items/g_h100_simple.iqm_0.skin b/models/items/g_h100_simple.iqm_0.skin new file mode 100644 index 0000000000..f8acadd097 --- /dev/null +++ b/models/items/g_h100_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_h100_simple \ No newline at end of file diff --git a/models/items/g_h100_simple.tga b/models/items/g_h100_simple.tga new file mode 100644 index 0000000000..08b780a3ef Binary files /dev/null and b/models/items/g_h100_simple.tga differ diff --git a/models/items/g_h1_simple.iqm b/models/items/g_h1_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/g_h1_simple.iqm differ diff --git a/models/items/g_h1_simple.iqm_0.skin b/models/items/g_h1_simple.iqm_0.skin new file mode 100644 index 0000000000..9532805c6e --- /dev/null +++ b/models/items/g_h1_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_h1_simple \ No newline at end of file diff --git a/models/items/g_h1_simple.tga b/models/items/g_h1_simple.tga new file mode 100644 index 0000000000..d8bbbc70fe Binary files /dev/null and b/models/items/g_h1_simple.tga differ diff --git a/models/items/g_h25_simple.iqm b/models/items/g_h25_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/g_h25_simple.iqm differ diff --git a/models/items/g_h25_simple.iqm_0.skin b/models/items/g_h25_simple.iqm_0.skin new file mode 100644 index 0000000000..0f33bc7556 --- /dev/null +++ b/models/items/g_h25_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_h25_simple \ No newline at end of file diff --git a/models/items/g_h25_simple.tga b/models/items/g_h25_simple.tga new file mode 100644 index 0000000000..63593cc87c Binary files /dev/null and b/models/items/g_h25_simple.tga differ diff --git a/models/items/g_h50_simple.iqm b/models/items/g_h50_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/g_h50_simple.iqm differ diff --git a/models/items/g_h50_simple.iqm_0.skin b/models/items/g_h50_simple.iqm_0.skin new file mode 100644 index 0000000000..b830541926 --- /dev/null +++ b/models/items/g_h50_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_h50_simple \ No newline at end of file diff --git a/models/items/g_h50_simple.tga b/models/items/g_h50_simple.tga new file mode 100644 index 0000000000..a8cbad9821 Binary files /dev/null and b/models/items/g_h50_simple.tga differ diff --git a/models/items/g_invincible_simple.iqm b/models/items/g_invincible_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/g_invincible_simple.iqm differ diff --git a/models/items/g_invincible_simple.iqm_0.skin b/models/items/g_invincible_simple.iqm_0.skin new file mode 100644 index 0000000000..6522c885d1 --- /dev/null +++ b/models/items/g_invincible_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_invincible_simple \ No newline at end of file diff --git a/models/items/g_invincible_simple.tga b/models/items/g_invincible_simple.tga new file mode 100644 index 0000000000..76cb4142f5 Binary files /dev/null and b/models/items/g_invincible_simple.tga differ diff --git a/models/items/g_strength_simple.iqm b/models/items/g_strength_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/g_strength_simple.iqm differ diff --git a/models/items/g_strength_simple.iqm_0.skin b/models/items/g_strength_simple.iqm_0.skin new file mode 100644 index 0000000000..bcdad79fa0 --- /dev/null +++ b/models/items/g_strength_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_strength_simple \ No newline at end of file diff --git a/models/items/g_strength_simple.tga b/models/items/g_strength_simple.tga new file mode 100644 index 0000000000..09aa45c1a8 Binary files /dev/null and b/models/items/g_strength_simple.tga differ diff --git a/models/items/item_armor_big_simple.iqm b/models/items/item_armor_big_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/item_armor_big_simple.iqm differ diff --git a/models/items/item_armor_big_simple.iqm_0.skin b/models/items/item_armor_big_simple.iqm_0.skin new file mode 100644 index 0000000000..ab88a068ea --- /dev/null +++ b/models/items/item_armor_big_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,item_armor_big_simple \ No newline at end of file diff --git a/models/items/item_armor_big_simple.tga b/models/items/item_armor_big_simple.tga new file mode 100644 index 0000000000..7bbd5b4d8f Binary files /dev/null and b/models/items/item_armor_big_simple.tga differ diff --git a/models/items/item_armor_large_simple.iqm b/models/items/item_armor_large_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/item_armor_large_simple.iqm differ diff --git a/models/items/item_armor_large_simple.iqm_0.skin b/models/items/item_armor_large_simple.iqm_0.skin new file mode 100644 index 0000000000..4ae65f397b --- /dev/null +++ b/models/items/item_armor_large_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,item_armor_large_simple \ No newline at end of file diff --git a/models/items/item_armor_large_simple.tga b/models/items/item_armor_large_simple.tga new file mode 100644 index 0000000000..80db5aba35 Binary files /dev/null and b/models/items/item_armor_large_simple.tga differ diff --git a/models/items/item_armor_medium_simple.iqm b/models/items/item_armor_medium_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/item_armor_medium_simple.iqm differ diff --git a/models/items/item_armor_medium_simple.iqm_0.skin b/models/items/item_armor_medium_simple.iqm_0.skin new file mode 100644 index 0000000000..8f258c141d --- /dev/null +++ b/models/items/item_armor_medium_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,item_armor_medium_simple \ No newline at end of file diff --git a/models/items/item_armor_medium_simple.tga b/models/items/item_armor_medium_simple.tga new file mode 100644 index 0000000000..ed3ce1f81f Binary files /dev/null and b/models/items/item_armor_medium_simple.tga differ diff --git a/models/items/item_armor_small_simple.iqm b/models/items/item_armor_small_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/items/item_armor_small_simple.iqm differ diff --git a/models/items/item_armor_small_simple.iqm_0.skin b/models/items/item_armor_small_simple.iqm_0.skin new file mode 100644 index 0000000000..50bbb53d4e --- /dev/null +++ b/models/items/item_armor_small_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,item_armor_small_simple \ No newline at end of file diff --git a/models/items/item_armor_small_simple.tga b/models/items/item_armor_small_simple.tga new file mode 100644 index 0000000000..d8a0f91f00 Binary files /dev/null and b/models/items/item_armor_small_simple.tga differ diff --git a/models/vehicles/bumblebee_body.dpm b/models/vehicles/bumblebee_body.dpm index 9962dd764a..016b6343d8 100644 Binary files a/models/vehicles/bumblebee_body.dpm and b/models/vehicles/bumblebee_body.dpm differ diff --git a/models/vehicles/bumblebee_plasma_left.dpm b/models/vehicles/bumblebee_plasma_left.dpm index 57df41a258..785699909c 100644 Binary files a/models/vehicles/bumblebee_plasma_left.dpm and b/models/vehicles/bumblebee_plasma_left.dpm differ diff --git a/models/vehicles/bumblebee_plasma_right.dpm b/models/vehicles/bumblebee_plasma_right.dpm index 1689979fcf..86cf3ddfc9 100644 Binary files a/models/vehicles/bumblebee_plasma_right.dpm and b/models/vehicles/bumblebee_plasma_right.dpm differ diff --git a/models/vehicles/bumblebee_ray.dpm b/models/vehicles/bumblebee_ray.dpm index 2e36eb5345..d6014b9c8f 100644 Binary files a/models/vehicles/bumblebee_ray.dpm and b/models/vehicles/bumblebee_ray.dpm differ diff --git a/models/weapons/crylink_simple.tga b/models/weapons/crylink_simple.tga new file mode 100644 index 0000000000..4dfe24eb0d Binary files /dev/null and b/models/weapons/crylink_simple.tga differ diff --git a/models/weapons/g_crylink_simple.iqm b/models/weapons/g_crylink_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_crylink_simple.iqm differ diff --git a/models/weapons/g_crylink_simple.iqm_0.skin b/models/weapons/g_crylink_simple.iqm_0.skin new file mode 100644 index 0000000000..e86b8b2b8e --- /dev/null +++ b/models/weapons/g_crylink_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_crylink_simple \ No newline at end of file diff --git a/models/weapons/g_crylink_simple.obj b/models/weapons/g_crylink_simple.obj new file mode 100644 index 0000000000..3bd17fcb31 --- /dev/null +++ b/models/weapons/g_crylink_simple.obj @@ -0,0 +1,14 @@ +# Blender3D v249 OBJ File: crylink_simple.blend +# www.blender3d.org +mtllib g_crylink_simple.mtl +v 0.000000 40.000000 14.999998 +v 0.000000 10.000001 15.000000 +v 0.000000 9.999999 -15.000000 +v 0.000000 40.000000 -15.000002 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +usemtl crylink_simple +s off +f 1/1 4/2 3/3 2/4 diff --git a/models/weapons/g_crylink_simple.tga b/models/weapons/g_crylink_simple.tga new file mode 100644 index 0000000000..f4054b7bec Binary files /dev/null and b/models/weapons/g_crylink_simple.tga differ diff --git a/models/weapons/g_electro_simple.iqm b/models/weapons/g_electro_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_electro_simple.iqm differ diff --git a/models/weapons/g_electro_simple.iqm_0.skin b/models/weapons/g_electro_simple.iqm_0.skin new file mode 100644 index 0000000000..0ae8f4afd5 --- /dev/null +++ b/models/weapons/g_electro_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_electro_simple \ No newline at end of file diff --git a/models/weapons/g_electro_simple.tga b/models/weapons/g_electro_simple.tga new file mode 100644 index 0000000000..b523c722e3 Binary files /dev/null and b/models/weapons/g_electro_simple.tga differ diff --git a/models/weapons/g_gl_simple.iqm b/models/weapons/g_gl_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_gl_simple.iqm differ diff --git a/models/weapons/g_gl_simple.iqm_0.skin b/models/weapons/g_gl_simple.iqm_0.skin new file mode 100644 index 0000000000..cbb574dffc --- /dev/null +++ b/models/weapons/g_gl_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_gl_simple \ No newline at end of file diff --git a/models/weapons/g_gl_simple.tga b/models/weapons/g_gl_simple.tga new file mode 100644 index 0000000000..e138aa42e7 Binary files /dev/null and b/models/weapons/g_gl_simple.tga differ diff --git a/models/weapons/g_hagar_simple.iqm b/models/weapons/g_hagar_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_hagar_simple.iqm differ diff --git a/models/weapons/g_hagar_simple.iqm_0.skin b/models/weapons/g_hagar_simple.iqm_0.skin new file mode 100644 index 0000000000..2eb9f30767 --- /dev/null +++ b/models/weapons/g_hagar_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_hagar_simple \ No newline at end of file diff --git a/models/weapons/g_hagar_simple.tga b/models/weapons/g_hagar_simple.tga new file mode 100644 index 0000000000..4c82e4beee Binary files /dev/null and b/models/weapons/g_hagar_simple.tga differ diff --git a/models/weapons/g_nex_simple.iqm b/models/weapons/g_nex_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_nex_simple.iqm differ diff --git a/models/weapons/g_nex_simple.iqm_0.skin b/models/weapons/g_nex_simple.iqm_0.skin new file mode 100644 index 0000000000..55a8cc4dd4 --- /dev/null +++ b/models/weapons/g_nex_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_nex_simple \ No newline at end of file diff --git a/models/weapons/g_nex_simple.tga b/models/weapons/g_nex_simple.tga new file mode 100644 index 0000000000..27c984f8a1 Binary files /dev/null and b/models/weapons/g_nex_simple.tga differ diff --git a/models/weapons/g_rl_simple.iqm b/models/weapons/g_rl_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_rl_simple.iqm differ diff --git a/models/weapons/g_rl_simple.iqm_0.skin b/models/weapons/g_rl_simple.iqm_0.skin new file mode 100644 index 0000000000..bcb054edc8 --- /dev/null +++ b/models/weapons/g_rl_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_rl_simple \ No newline at end of file diff --git a/models/weapons/g_rl_simple.tga b/models/weapons/g_rl_simple.tga new file mode 100644 index 0000000000..d65fa8788b Binary files /dev/null and b/models/weapons/g_rl_simple.tga differ diff --git a/models/weapons/g_shotgun_simple.iqm b/models/weapons/g_shotgun_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_shotgun_simple.iqm differ diff --git a/models/weapons/g_shotgun_simple.iqm_0.skin b/models/weapons/g_shotgun_simple.iqm_0.skin new file mode 100644 index 0000000000..3721ffc99e --- /dev/null +++ b/models/weapons/g_shotgun_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_shotgun_simple \ No newline at end of file diff --git a/models/weapons/g_shotgun_simple.tga b/models/weapons/g_shotgun_simple.tga new file mode 100644 index 0000000000..37da2f8629 Binary files /dev/null and b/models/weapons/g_shotgun_simple.tga differ diff --git a/models/weapons/g_uzi_simple.iqm b/models/weapons/g_uzi_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_uzi_simple.iqm differ diff --git a/models/weapons/g_uzi_simple.iqm_0.skin b/models/weapons/g_uzi_simple.iqm_0.skin new file mode 100644 index 0000000000..043974b349 --- /dev/null +++ b/models/weapons/g_uzi_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_uzi_simple \ No newline at end of file diff --git a/models/weapons/g_uzi_simple.tga b/models/weapons/g_uzi_simple.tga new file mode 100644 index 0000000000..d9b5df404a Binary files /dev/null and b/models/weapons/g_uzi_simple.tga differ diff --git a/models/weapons/h_kleinbottle.iqm b/models/weapons/h_kleinbottle.iqm new file mode 100644 index 0000000000..523ef998a8 Binary files /dev/null and b/models/weapons/h_kleinbottle.iqm differ diff --git a/models/weapons/h_kleinbottle.iqm.framegroups b/models/weapons/h_kleinbottle.iqm.framegroups new file mode 100644 index 0000000000..0a59625b6a --- /dev/null +++ b/models/weapons/h_kleinbottle.iqm.framegroups @@ -0,0 +1,4 @@ +1 8 20 0 // fire +9 5 20 0 // fire2 +15 200 20 1 // idle +215 40 20 0 // reload diff --git a/models/weapons/v_kleinbottle.md3 b/models/weapons/v_kleinbottle.md3 new file mode 100644 index 0000000000..b42affbb46 Binary files /dev/null and b/models/weapons/v_kleinbottle.md3 differ diff --git a/mutator_new_toys.cfg b/mutator_new_toys.cfg new file mode 100644 index 0000000000..c4f20995af --- /dev/null +++ b/mutator_new_toys.cfg @@ -0,0 +1,163 @@ +set g_new_toys 0 "Mutator 'New Toys': enable extra fun guns" +set g_new_toys_autoreplace 2 "0: never replace, 1: always auto replace guns by available new toys, 2: randomly auto replace guns by available new toys" + +set g_weaponreplace_hlac "" +set g_weaponreplace_minelayer "" +set g_weaponreplace_rifle "" +set g_weaponreplace_seeker "" + +set g_start_weapon_hlac -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE +set g_start_weapon_minelayer -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_rifle -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE +set g_start_weapon_seeker -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" + +// {{{ hlac +set g_balance_hlac_primary_spread_min 0.01 +set g_balance_hlac_primary_spread_max 0.25 +set g_balance_hlac_primary_spread_add 0.0045 +set g_balance_hlac_primary_spread_crouchmod 0.25 + +set g_balance_hlac_primary_damage 18 +set g_balance_hlac_primary_edgedamage 9 +set g_balance_hlac_primary_force 90 +set g_balance_hlac_primary_radius 70 +set g_balance_hlac_primary_speed 9000 +set g_balance_hlac_primary_lifetime 5 + +set g_balance_hlac_primary_refire 0.15 +set g_balance_hlac_primary_animtime 0.4 +set g_balance_hlac_primary_ammo 1 + +set g_balance_hlac_secondary 1 +set g_balance_hlac_secondary_spread 0.15 +set g_balance_hlac_secondary_spread_crouchmod 0.5 + +set g_balance_hlac_secondary_damage 15 +set g_balance_hlac_secondary_edgedamage 7.5 +set g_balance_hlac_secondary_force 90 +set g_balance_hlac_secondary_radius 70 +set g_balance_hlac_secondary_speed 9000 +set g_balance_hlac_secondary_lifetime 5 + +set g_balance_hlac_secondary_refire 1 +set g_balance_hlac_secondary_animtime 0.3 +set g_balance_hlac_secondary_ammo 10 +set g_balance_hlac_secondary_shots 6 + +set g_balance_hlac_reload_ammo 0 //default: 20 +set g_balance_hlac_reload_time 2 +// }}} +// {{{ minelayer +set g_balance_minelayer_damage 40 +set g_balance_minelayer_edgedamage 20 +set g_balance_minelayer_force 250 +set g_balance_minelayer_radius 175 +set g_balance_minelayer_proximityradius 150 +set g_balance_minelayer_speed 1000 +set g_balance_minelayer_lifetime 10 +set g_balance_minelayer_lifetime_countdown 0.5 +set g_balance_minelayer_refire 1.5 +set g_balance_minelayer_animtime 0.4 +set g_balance_minelayer_ammo 4 +set g_balance_minelayer_health 15 +set g_balance_minelayer_limit 3 // 0 disables the limit +set g_balance_minelayer_protection 0 // don't explode if the mine would hurt the owner or a team mate +set g_balance_minelayer_damageforcescale 0 +set g_balance_minelayer_detonatedelay -1 // 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_minelayer_time 0.5 +set g_balance_minelayer_remote_damage 45 +set g_balance_minelayer_remote_edgedamage 40 +set g_balance_minelayer_remote_radius 200 +set g_balance_minelayer_remote_force 300 +set g_balance_minelayer_reload_ammo 0 //default: 15 +set g_balance_minelayer_reload_time 2 +// }}} +// {{{ rifle +set g_balance_rifle_bursttime 0 +set g_balance_rifle_primary_tracer 1 +set g_balance_rifle_primary_shots 1 +set g_balance_rifle_primary_damage 40 +set g_balance_rifle_primary_headshotaddeddamage 40 +set g_balance_rifle_primary_spread 0 +set g_balance_rifle_primary_force 100 +set g_balance_rifle_primary_speed 40000 +set g_balance_rifle_primary_lifetime 5 +set g_balance_rifle_primary_refire 1.2 +set g_balance_rifle_primary_animtime 0.4 +set g_balance_rifle_primary_ammo 10 +set g_balance_rifle_primary_bulletconstant 110 // 62.2qu +set g_balance_rifle_primary_burstcost 0 +set g_balance_rifle_primary_bullethail 0 // empty magazine on shot +set g_balance_rifle_secondary 1 +set g_balance_rifle_secondary_reload 0 +set g_balance_rifle_secondary_tracer 0 +set g_balance_rifle_secondary_shots 4 +set g_balance_rifle_secondary_damage 10 +set g_balance_rifle_secondary_headshotaddeddamage 20 +set g_balance_rifle_secondary_spread 0.04 +set g_balance_rifle_secondary_force 50 +set g_balance_rifle_secondary_speed 20000 +set g_balance_rifle_secondary_lifetime 5 +set g_balance_rifle_secondary_refire 0.9 +set g_balance_rifle_secondary_animtime 0.3 +set g_balance_rifle_secondary_ammo 10 +set g_balance_rifle_secondary_bulletconstant 110 // 15.5qu +set g_balance_rifle_secondary_burstcost 0 +set g_balance_rifle_secondary_bullethail 0 // empty magazine on shot +set g_balance_rifle_reload_ammo 80 //default: 80 +set g_balance_rifle_reload_time 2 +// }}} +// {{{ seeker +set g_balance_seeker_type 0 // 0 = old seeker, 1 = new seeker +set g_balance_seeker_flac_ammo 0.5 +set g_balance_seeker_flac_animtime 0.1 +set g_balance_seeker_flac_damage 15 +set g_balance_seeker_flac_edgedamage 10 +set g_balance_seeker_flac_force 50 +set g_balance_seeker_flac_lifetime 0.1 +set g_balance_seeker_flac_lifetime_rand 0.05 +set g_balance_seeker_flac_radius 100 +set g_balance_seeker_flac_refire 0.1 +set g_balance_seeker_flac_speed 3000 +set g_balance_seeker_flac_speed_up 1000 +set g_balance_seeker_flac_speed_z 0 +set g_balance_seeker_flac_spread 0.4 +set g_balance_seeker_tag_ammo 1 +set g_balance_seeker_tag_animtime 0.2 +set g_balance_seeker_tag_damageforcescale 4 +set g_balance_seeker_tag_health 5 +set g_balance_seeker_tag_lifetime 15 +set g_balance_seeker_tag_refire 0.75 // LOG: 0.7 -> 0.75 +set g_balance_seeker_tag_speed 5000 +set g_balance_seeker_tag_spread 0 +set g_balance_seeker_tag_tracker_lifetime 10 +set g_balance_seeker_missile_accel 1400 +set g_balance_seeker_missile_ammo 2 +set g_balance_seeker_missile_animtime 0.2 +set g_balance_seeker_missile_count 3 // LOG: 8 -> 3 +set g_balance_seeker_missile_damage 30 // LOG: 15 -> 30 +set g_balance_seeker_missile_damageforcescale 4 +set g_balance_seeker_missile_decel 1400 +set g_balance_seeker_missile_delay 0.25 +set g_balance_seeker_missile_edgedamage 10 +set g_balance_seeker_missile_force 150 // LOG: 100 -> 150 +set g_balance_seeker_missile_health 5 +set g_balance_seeker_missile_lifetime 15 +set g_balance_seeker_missile_proxy 0 +set g_balance_seeker_missile_proxy_delay 0.2 +set g_balance_seeker_missile_proxy_maxrange 45 +set g_balance_seeker_missile_radius 80 +set g_balance_seeker_missile_refire 0.5 +set g_balance_seeker_missile_smart 1 +set g_balance_seeker_missile_smart_mindist 800 +set g_balance_seeker_missile_smart_trace_max 2500 +set g_balance_seeker_missile_smart_trace_min 1000 +set g_balance_seeker_missile_speed 700 +set g_balance_seeker_missile_speed_up 300 +set g_balance_seeker_missile_speed_z 0 +set g_balance_seeker_missile_speed_max 1300 // LOG: 1400 -> 1300 +set g_balance_seeker_missile_spread 0 +set g_balance_seeker_missile_turnrate 0.65 +set g_balance_seeker_reload_ammo 0 //default: 15 +set g_balance_seeker_reload_time 2 +// End new seeker diff --git a/physicsXDF.cfg b/physicsXDF.cfg new file mode 100644 index 0000000000..0acb6e9535 --- /dev/null +++ b/physicsXDF.cfg @@ -0,0 +1,42 @@ +g_mod_physics XDF + +// - Xonotic DeFrag - +// Nexrun tweaked to suit CPM + +sv_gravity 800 +sv_maxspeed 320 +// CPMA: 320 +sv_maxairspeed 320 +// CPMA: 320 +sv_stopspeed 100 +sv_accelerate 15 +sv_airaccelerate 1 +sv_friction 5.8 +edgefriction 1 +sv_stepheight 26 +// CPMA: 18 +sv_jumpvelocity 270 +sv_wateraccelerate 4 +sv_waterfriction 1 +sv_airaccel_sideways_friction 0 +sv_airaccel_qw 0.95 +sv_airaccel_qw_stretchfactor 0 +// CPMA: 1 +sv_airstopaccelerate 2.5 +sv_airstrafeaccelerate 70 +sv_maxairstrafespeed 30 +sv_airstrafeaccel_qw 1 +sv_aircontrol 150 +sv_aircontrol_penalty 0 +sv_aircontrol_power 2 +sv_airspeedlimit_nonqw 0 +sv_warsowbunny_turnaccel 0 +sv_warsowbunny_accel 0.1593 +sv_warsowbunny_topspeed 925 +sv_warsowbunny_backtosideratio 0.8 +sv_friction_on_land 0 +sv_doublejump 1 +sv_jumpspeedcap_min 0 +sv_jumpspeedcap_max 0.5 +sv_jumpspeedcap_max_disable_on_ramps 1 +g_teleport_maxspeed 600 diff --git a/physicsXDFLight.cfg b/physicsXDFLight.cfg new file mode 100644 index 0000000000..cac90b4ee7 --- /dev/null +++ b/physicsXDFLight.cfg @@ -0,0 +1,42 @@ +g_mod_physics XDFLight + +// - Xonotic DeFrag Light - +// Nexrun tweaked to suit CPM + +sv_gravity 800 +sv_maxspeed 320 +// CPMA: 320 +sv_maxairspeed 320 +// CPMA: 320 +sv_stopspeed 100 +sv_accelerate 15 +sv_airaccelerate 1 +sv_friction 8 +edgefriction 1 +sv_stepheight 26 +// CPMA: 18 +sv_jumpvelocity 270 +sv_wateraccelerate 4 +sv_waterfriction 1 +sv_airaccel_sideways_friction 0 +sv_airaccel_qw -0.9146875 +sv_airaccel_qw_stretchfactor 0 +// CPMA: 1 +sv_airstopaccelerate 6.5625 // matches strafe-stopping speed +sv_airstrafeaccelerate 14 +sv_maxairstrafespeed 150 +sv_airstrafeaccel_qw -0.987 +sv_aircontrol 100 +sv_aircontrol_penalty 100 +sv_aircontrol_power 2.5 +sv_airspeedlimit_nonqw 0 +sv_warsowbunny_turnaccel 0 +sv_warsowbunny_accel 0.1593 +sv_warsowbunny_topspeed 925 +sv_warsowbunny_backtosideratio 0.8 +sv_friction_on_land 0 +sv_doublejump 0 +sv_jumpspeedcap_min "" +sv_jumpspeedcap_max "" +sv_jumpspeedcap_max_disable_on_ramps 1 +g_teleport_maxspeed 0 diff --git a/physicsXPM.cfg b/physicsXPM.cfg deleted file mode 100644 index 55e2ef01d3..0000000000 --- a/physicsXPM.cfg +++ /dev/null @@ -1,40 +0,0 @@ -g_mod_physics XPM -// Nexrun tweaked to suit CPM - -sv_gravity 800 -sv_maxspeed 320 -// CPMA: 320 -sv_maxairspeed 320 -// CPMA: 320 -sv_stopspeed 100 -sv_accelerate 15 -sv_airaccelerate 1 -sv_friction 8 -edgefriction 1 -sv_stepheight 26 -// CPMA: 18 -sv_jumpvelocity 270 -sv_wateraccelerate 4 -sv_waterfriction 1 -sv_airaccel_sideways_friction 0 -sv_airaccel_qw 0.95 -sv_airaccel_qw_stretchfactor 0 -// CPMA: 1 -sv_airstopaccelerate 2.5 -sv_airstrafeaccelerate 70 -sv_maxairstrafespeed 30 -sv_airstrafeaccel_qw 1 -sv_aircontrol 150 -sv_aircontrol_penalty 0 -sv_aircontrol_power 2 -sv_airspeedlimit_nonqw 0 -sv_warsowbunny_turnaccel 0 -sv_warsowbunny_accel 0.1593 -sv_warsowbunny_topspeed 925 -sv_warsowbunny_backtosideratio 0.8 -sv_friction_on_land 0 -sv_doublejump 1 -sv_jumpspeedcap_min 0 -sv_jumpspeedcap_max 0.5 -sv_jumpspeedcap_max_disable_on_ramps 1 -g_teleport_maxspeed 400 diff --git a/physicsXPMLight.cfg b/physicsXPMLight.cfg deleted file mode 100644 index d0f31c2c0b..0000000000 --- a/physicsXPMLight.cfg +++ /dev/null @@ -1,41 +0,0 @@ -g_mod_physics XPMLight -// Nexrun tweaked to suit CPM - -sv_gravity 800 -sv_maxspeed 320 -// CPMA: 320 -sv_maxairspeed 320 -// CPMA: 320 -sv_stopspeed 100 -sv_accelerate 15 -sv_airaccelerate 1 -sv_friction 8 -edgefriction 1 -sv_stepheight 26 -// CPMA: 18 -sv_jumpvelocity 270 -sv_wateraccelerate 4 -sv_waterfriction 1 -sv_airaccel_sideways_friction 0 -sv_airaccel_qw -0.9146875 -sv_airaccel_qw_stretchfactor 0 -sv_airaccel_qw_stretchfactor 0 -// CPMA: 1 -sv_airstopaccelerate 6.5625 // matches strafe-stopping speed -sv_airstrafeaccelerate 14 -sv_maxairstrafespeed 150 -sv_airstrafeaccel_qw -0.987 -sv_aircontrol 100 -sv_aircontrol_penalty 100 -sv_aircontrol_power 2.5 -sv_airspeedlimit_nonqw 0 -sv_warsowbunny_turnaccel 0 -sv_warsowbunny_accel 0.1593 -sv_warsowbunny_topspeed 925 -sv_warsowbunny_backtosideratio 0.8 -sv_friction_on_land 0 -sv_doublejump 0 -sv_jumpspeedcap_min "" -sv_jumpspeedcap_max "" -sv_jumpspeedcap_max_disable_on_ramps 1 -g_teleport_maxspeed 0 diff --git a/qcsrc/Makefile b/qcsrc/Makefile index 618e3ad4e2..27f6fc993a 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -3,7 +3,7 @@ FTEQCC ?= fteqcc PERL ?= perl FTEQCCFLAGS_WATERMARK ?= -DWATERMARK='"$(shell git describe)"' -DCVAR_POPCON -FTEQCCFLAGS ?= -Werror -Wno-Q302 -O3 -fno-fastarrays $(FTEQCCFLAGS_EXTRA) $(FTEQCCFLAGS_WATERMARK) +FTEQCCFLAGS ?= -Werror -Wno-Q302 -O3 -Ono-return_only -fno-fastarrays $(FTEQCCFLAGS_EXTRA) $(FTEQCCFLAGS_WATERMARK) FTEQCCFLAGS_PROGS ?= FTEQCCFLAGS_MENU ?= diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index 8a9c350bce..835d2855ed 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -2,6 +2,19 @@ // BEGIN REQUIRED CSQC FUNCTIONS //include "main.qh" +entity clearentity_ent; +void clearentity(entity e) +{ + if not(clearentity_ent) + { + clearentity_ent = spawn(); + clearentity_ent.classname = "clearentity"; + } + float n = e.entnum; + copyentity(clearentity_ent, e); + e.entnum = n; +} + #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED void menu_show_error() { @@ -74,7 +87,6 @@ void ConsoleCommand_macro_init(); void CSQC_Init(void) { prvm_language = cvar_string("prvm_language"); - #ifdef USE_FTE #pragma target ID __engine_check = checkextension("DP_SV_WRITEPICTURE"); @@ -105,7 +117,7 @@ void CSQC_Init(void) ClientProgsDB = db_load("client.db"); compressShortVector_init(); - drawfont = FONT_USER+1; + draw_endBoldFont(); menu_visible = FALSE; menu_show = menu_show_error; menu_action = menu_sub_null; @@ -140,8 +152,8 @@ void CSQC_Init(void) GetTeam(COLOR_SPECTATOR, true); // add specs first // needs to be done so early because of the constants they create - RegisterWeapons(); - RegisterGametypes(); + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); WaypointSprite_Load(); @@ -165,7 +177,7 @@ void CSQC_Init(void) DamageInfo_Precache(); Vehicles_Precache(); turrets_precache(); - Announcer_Precache(); + Announcer_Precache(); Tuba_Precache(); if(autocvar_cl_reticle) @@ -660,7 +672,7 @@ void Ent_ReadAccuracy(void) return; } - for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w, f *= 2) + for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) { if(sf & f) { @@ -672,6 +684,10 @@ void Ent_ReadAccuracy(void) else weapon_accuracy[w] = (b - 1.0) / 100.0; } + if(f == 0x800000) + f = 1; + else + f *= 2; } } @@ -686,6 +702,9 @@ void CSQC_Ent_Update(float bIsNewEntity) float savetime; t = ReadByte(); + if(autocvar_developer_csqcentities) + print(sprintf("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t)); + // set up the "time" global for received entities to be correct for interpolation purposes savetime = time; if(servertime) @@ -707,6 +726,7 @@ void CSQC_Ent_Update(float bIsNewEntity) //print(_("A CSQC entity changed its type!\n")); print(sprintf(_("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n"), num_for_edict(self), self.entnum, self.enttype, t)); Ent_Remove(); + clearentity(self); bIsNewEntity = 1; } } @@ -753,7 +773,9 @@ void CSQC_Ent_Update(float bIsNewEntity) case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; case ENT_CLIENT_TURRET: ent_turret(); break; - case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); 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)); @@ -779,6 +801,9 @@ void Ent_Remove() // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(self) as well. void CSQC_Ent_Remove() { + if(autocvar_developer_csqcentities) + print(sprintf("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype)); + if(wasfreed(self)) { print("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); @@ -801,17 +826,26 @@ void Gamemode_Init() // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string. void CSQC_Parse_StuffCmd(string strMessage) { + if(autocvar_developer_csqcentities) + print(sprintf("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage)); + localcmd(strMessage); } // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string. void CSQC_Parse_Print(string strMessage) { + if(autocvar_developer_csqcentities) + print(sprintf("CSQC_Parse_Print(\"%s\")\n", strMessage)); + print(ColorTranslateRGB(strMessage)); } // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided. void CSQC_Parse_CenterPrint(string strMessage) { + if(autocvar_developer_csqcentities) + print(sprintf("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage)); + centerprint_hud(strMessage); } @@ -1098,6 +1132,9 @@ float CSQC_Parse_TempEntity() float nTEID; nTEID = ReadByte(); + if(autocvar_developer_csqcentities) + print(sprintf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID)); + // NOTE: Could just do return instead of break... switch(nTEID) { diff --git a/qcsrc/client/View.qc b/qcsrc/client/View.qc index e04a9704d6..996302ca32 100644 --- a/qcsrc/client/View.qc +++ b/qcsrc/client/View.qc @@ -1183,7 +1183,8 @@ void CSQC_UpdateView(float w, float h) if(autocvar_crosshair_hitindication) { - vector hitindication_color = stov(autocvar_crosshair_hitindication_color); + vector hitindication_color = ((autocvar_crosshair_color_per_weapon) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color)); + if(hitindication_crosshair_time < hit_time) { if(time - hit_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old @@ -1468,6 +1469,8 @@ void CSQC_UpdateView(float w, float h) CSQC_RAPTOR_HUD(); else if(hud == HUD_BUMBLEBEE) CSQC_BUMBLE_HUD(); + else if(hud == HUD_BUMBLEBEE_GUN) + CSQC_BUMBLE_GUN_HUD(); } cl_notice_run(); diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index 3e109e7805..db1ae41336 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -134,52 +134,10 @@ void Announcer_Time() } } -float redflag_prev; -float blueflag_prev; -void carrierAnnouncer() { - float stat_items, redflag, blueflag; - float pickup; - string item; - - if not(autocvar_cl_notify_carried_items) - return; - - stat_items = getstati(STAT_ITEMS); - - redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3; - blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3; - - if (redflag == 3 && redflag != redflag_prev) { - item = _("^1RED^7 flag"); - pickup = (redflag_prev == 2); - } - - if (blueflag == 3 && blueflag != blueflag_prev) { - item = _("^4BLUE^7 flag"); - pickup = (blueflag_prev == 2); - } - - if (item) - { - if (pickup) { - if (autocvar_cl_notify_carried_items & 2) - centerprint_hud(sprintf(_("You picked up the %s!"), item)); - } - else { - if (autocvar_cl_notify_carried_items & 1) - centerprint_hud(sprintf(_("You got the %s!"), item)); - } - } - - blueflag_prev = blueflag; - redflag_prev = redflag; -} - void Announcer() { Announcer_Gamestart(); Announcer_Time(); - carrierAnnouncer(); } void Announcer_Precache () diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 43dd0dd183..5d5637473a 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -54,7 +54,6 @@ float autocvar_cl_gunalign; float autocvar_cl_hidewaypoints; float autocvar_cl_lockview; float autocvar_cl_nogibs; -float autocvar_cl_notify_carried_items; float autocvar_cl_particlegibs; float autocvar_cl_particles_oldnexbeam; float autocvar_cl_particles_quality; @@ -67,6 +66,7 @@ float autocvar_cl_reticle_stretch; float autocvar_cl_stripcolorcodes; var float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6; var float autocvar_cl_vehicle_spiderbot_cross_size = 1; +var float autocvar_cl_vehicles_hud_tactical = 1; float autocvar_cl_velocityzoom; var float autocvar_cl_velocityzoom_type = 3; float autocvar_cl_velocityzoom_speed; @@ -96,6 +96,7 @@ float autocvar_crosshair_effect_speed; var float autocvar_crosshair_enabled = 1; float autocvar_crosshair_hitindication; string autocvar_crosshair_hitindication_color; +string autocvar_crosshair_hitindication_per_weapon_color; float autocvar_crosshair_hitindication_speed; float autocvar_crosshair_hittest; float autocvar_crosshair_hittest_blur; @@ -153,8 +154,8 @@ float autocvar_g_waypointsprite_normdistance; float autocvar_g_waypointsprite_scale; float autocvar_g_waypointsprite_spam; float autocvar_g_waypointsprite_timealphaexponent; -//float autocvar_g_waypointsprites_turrets; -//float autocvar_g_waypointsprites_turrets_maxdist; +var float autocvar_g_waypointsprite_turrets = TRUE; +var float autocvar_g_waypointsprite_turrets_maxdist = 5000; float autocvar_hud_colorflash_alpha; float autocvar_hud_configure_checkcollisions; @@ -279,6 +280,7 @@ string autocvar_hud_panel_powerups_progressbar_superweapons; float autocvar_hud_panel_powerups_text; float autocvar_hud_panel_pressedkeys; float autocvar_hud_panel_pressedkeys_aspect; +float autocvar_hud_panel_pressedkeys_attack; float autocvar_hud_panel_racetimer; float autocvar_hud_panel_radar; float autocvar_hud_panel_radar_foreground_alpha; @@ -365,6 +367,7 @@ var float autocvar_scoreboard_highlight_alpha_self = 0.25; float autocvar_scoreboard_offset_left; float autocvar_scoreboard_offset_right; float autocvar_scoreboard_offset_vertical; +float autocvar_scoreboard_respawntime_decimals; float autocvar_v_flipped; float autocvar_vid_conheight; float autocvar_vid_conwidth; @@ -394,7 +397,9 @@ float autocvar_cl_forceplayercolors; string autocvar_cl_forcemyplayermodel; float autocvar_cl_forcemyplayerskin; float autocvar_cl_forcemyplayercolors; +float autocvar__cl_color; float autocvar__cl_playerskin; string autocvar__cl_playermodel; float autocvar_cl_precacheplayermodels; float autocvar_cl_deathglow; +float autocvar_developer_csqcentities; diff --git a/qcsrc/client/command/cl_cmd.qc b/qcsrc/client/command/cl_cmd.qc index 638b7f996c..23b9b8c70a 100644 --- a/qcsrc/client/command/cl_cmd.qc +++ b/qcsrc/client/command/cl_cmd.qc @@ -57,6 +57,45 @@ void LocalCommand_blurtest(float request) #endif } +void LocalCommand_create_scrshot_ent(float request) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + float fh; + string filename = strcat(MapInfo_Map_bspname, "_scrshot_ent.txt"); + fh = fopen(filename, FILE_WRITE); + + if(fh >= 0) + { + fputs(fh, "{\n"); + fputs(fh, strcat("\"classname\" \"info_autoscreenshot\"\n")); + fputs(fh, strcat("\"origin\" \"", strcat(ftos(view_origin_x), " ", ftos(view_origin_y), " ", ftos(view_origin_z)), "\"\n")); + fputs(fh, strcat("\"angles\" \"", strcat(ftos(view_angles_x), " ", ftos(view_angles_y), " ", ftos(view_angles_z)), "\"\n")); + fputs(fh, "}\n"); + + print("Completed screenshot entity dump in ^2data/data/", MapInfo_Map_bspname, "_scrshot_ent.txt^7.\n"); + + fclose(fh); + } + else + { + print("^1Error: ^7Could not dump to file!\n"); + } + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 cl_cmd create_scrshot_ent\n"); + print(" No arguments required.\n"); + return; + } + } +} + void LocalCommand_debugmodel(float request, float argc) { switch(request) @@ -310,6 +349,7 @@ void LocalCommand_(float request) // but for 0.5 compat, we need vyes and vno here as they were replaced... REMOVE THEM AFTER 0.6 RELEASE!!!! #define CLIENT_COMMANDS(request,arguments) \ CLIENT_COMMAND("blurtest", LocalCommand_blurtest(request), "Feature for testing blur postprocessing") \ + CLIENT_COMMAND("create_scrshot_ent", LocalCommand_create_scrshot_ent(request), "Create an entity at this location for automatic screenshots") \ CLIENT_COMMAND("debugmodel", LocalCommand_debugmodel(request, arguments), "Spawn a debug model manually") \ CLIENT_COMMAND("handlevote", LocalCommand_handlevote(request, arguments), "System to handle selecting a vote or option") \ CLIENT_COMMAND("hud", LocalCommand_hud(request, arguments), "Commands regarding/controlling the HUD system") \ diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index b93bca47fe..9c87d13add 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -14,6 +14,9 @@ void CSQCPlayer_LOD_Apply(void) string modelname = self.model; string s; + vector mi = self.mins; + vector ma = self.maxs; + // set modelindex self.lodmodelindex0 = self.modelindex; self.lodmodelindex1 = self.modelindex; @@ -39,6 +42,7 @@ void CSQCPlayer_LOD_Apply(void) } setmodel(self, modelname); // make everything normal again + setsize(self, mi, ma); } // apply LOD @@ -129,12 +133,15 @@ void CSQCPlayer_ForceModel_Apply(float islocalplayer) { if(islocalplayer) { - // trust server's idea of "own player model" - forceplayermodels_modelisgoodmodel = self.forceplayermodels_isgoodmodel; - forceplayermodels_model = self.forceplayermodels_savemodel; - forceplayermodels_modelindex = self.forceplayermodels_savemodelindex; - forceplayermodels_skin = self.forceplayermodels_saveskin; - forceplayermodels_attempted = 1; + if(!isdemo()) // this is mainly cheat protection; not needed for demos + { + // trust server's idea of "own player model" + forceplayermodels_modelisgoodmodel = self.forceplayermodels_isgoodmodel; + forceplayermodels_model = self.forceplayermodels_savemodel; + forceplayermodels_modelindex = self.forceplayermodels_savemodelindex; + forceplayermodels_skin = self.forceplayermodels_saveskin; + forceplayermodels_attempted = 1; + } } } @@ -166,7 +173,17 @@ void CSQCPlayer_ForceModel_Apply(float islocalplayer) } // apply it - if(autocvar_cl_forcemyplayermodel != "" && forceplayermodels_myisgoodmodel && islocalplayer) + float isfriend; + float cm; + cm = self.forceplayermodels_savecolormap; + cm = (cm >= 1024) ? cm : (stof(getplayerkeyvalue(self.colormap - 1, "colors")) + 1024); + + if(teamplay) + isfriend = (cm == 1024 + 17 * myteam); + else + isfriend = islocalplayer; + + if(autocvar_cl_forcemyplayermodel != "" && forceplayermodels_myisgoodmodel && isfriend) { self.model = forceplayermodels_mymodel; self.modelindex = forceplayermodels_mymodelindex; @@ -192,7 +209,56 @@ void CSQCPlayer_ForceModel_Apply(float islocalplayer) } // forceplayercolors too - if(!teamplay) + if(teamplay) + { + // own team's color is never forced + float forcecolor_friend = 0; + float forcecolor_enemy = 0; + float teams_count = 0; + entity tm; + + for(tm = teams.sort_next; tm; tm = tm.sort_next) + if(tm.team != COLOR_SPECTATOR) + ++teams_count; + + if(autocvar_cl_forcemyplayercolors) + forcecolor_friend = 1024 + autocvar_cl_forcemyplayercolors; + if(autocvar_cl_forceplayercolors && teams_count == 2) + forcecolor_enemy = 1024 + autocvar__cl_color; + + if(forcecolor_enemy && !forcecolor_friend) + { + // only enemy color is forced? + // verify it is not equal to the friend color + if(forcecolor_enemy == 1024 + 17 * myteam) + forcecolor_enemy = 0; + } + + if(forcecolor_friend && !forcecolor_enemy) + { + // only friend color is forced? + // verify it is not equal to the enemy color + for(tm = teams.sort_next; tm; tm = tm.sort_next) + // note: we even compare against our own team. + // if we rejected because we matched our OWN team color, + // this is not bad; we then simply keep our color as is + // anyway. + if(forcecolor_friend == 1024 + 17 * tm.team) + forcecolor_friend = 0; + } + + if(cm == 1024 + 17 * myteam) + { + if(forcecolor_friend) + self.colormap = forcecolor_friend; + } + else + { + if(forcecolor_enemy) + self.colormap = forcecolor_enemy; + } + } + else { if(autocvar_cl_forcemyplayercolors && islocalplayer) self.colormap = 1024 + autocvar_cl_forcemyplayercolors; diff --git a/qcsrc/client/damage.qc b/qcsrc/client/damage.qc index fc8b83b5b6..ae83d45eea 100644 --- a/qcsrc/client/damage.qc +++ b/qcsrc/client/damage.qc @@ -229,7 +229,7 @@ void Ent_DamageInfo(float isNew) sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_MIN); pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1); break; - + case DEATH_WAKIGUN: sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); pointparticles(particleeffectnum("wakizashi_gun_impact"), self.origin, w_backoff * 1000, 1); @@ -267,6 +267,10 @@ void Ent_DamageInfo(float isNew) sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_MIN); pointparticles(particleeffectnum("explosion_big"), self.origin, w_backoff * 1000, 1); break; + case DEATH_BUMB_GUN: + sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("bigplasma_impact"), self.origin, w_backoff * 1000, 1); + break; } } diff --git a/qcsrc/client/hud.qc b/qcsrc/client/hud.qc index 0101c08708..16cf6d2e9c 100644 --- a/qcsrc/client/hud.qc +++ b/qcsrc/client/hud.qc @@ -432,7 +432,8 @@ float GetAmmoTypeForWep(float i) void HUD_Weapons(void) { // declarations - float weapons_stat = getstati(STAT_WEAPONS); + WEPSET_DECLARE_A(weapons_stat); + WEPSET_COPY_AS(weapons_stat); float i, f, a, j, factor; float screen_ar, center_x, center_y; float weapon_count, weapon_id, weapon_alpha; @@ -475,6 +476,8 @@ void HUD_Weapons(void) HUD_Panel_UpdateCvars(weapons); HUD_Panel_ApplyFadeAlpha(); + draw_beginBoldFont(); + // calculate fading effect to weapon images for when the panel is idle if(autocvar_hud_panel_weapons_fade) { @@ -519,9 +522,9 @@ void HUD_Weapons(void) { if(autocvar__hud_configure) { - if (weapons_stat == 0) // create some fake weapons anyway - for(i = 0; i <= WEP_LAST-WEP_FIRST; i += floor((WEP_LAST-WEP_FIRST)/5)) - weapons_stat |= power2of(i); + if (WEPSET_EMPTY_A(weapons_stat)) + for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5)) + WEPSET_OR_AW(weapons_stat, i); if(menu_enabled != 2) HUD_Panel_DrawBg(1); // also draw the bg of the entire panel @@ -529,7 +532,7 @@ void HUD_Weapons(void) // do we own this weapon? for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) - if(weapons_stat & weaponorder[i].weapons) + if(WEPSET_CONTAINS_AW(weapons_stat, weaponorder[i].weapon)) ++weapon_count; // add it anyway if weaponcomplain is shown @@ -678,7 +681,7 @@ void HUD_Weapons(void) // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon if (autocvar_hud_panel_weapons_onlyowned - && !((weapons_stat & self.weapons) + && !(WEPSET_CONTAINS_AW(weapons_stat, self.weapon) || (self.weapon == complain_weapon && time - complain_weapon_time < when + fadetime && autocvar_hud_panel_weapons_complainbubble))) @@ -714,7 +717,7 @@ void HUD_Weapons(void) } // drawing all the weapon items - if(weapons_stat & self.weapons) + if(WEPSET_CONTAINS_AW(weapons_stat, self.weapon)) { // draw the weapon image drawpic_aspect_skin(weapon_pos, strcat("weapon", self.netname), weapon_size, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); @@ -807,6 +810,8 @@ void HUD_Weapons(void) ++column; } } + + draw_endBoldFont(); } // Ammo (#1) @@ -907,6 +912,9 @@ void HUD_Ammo(void) HUD_Panel_UpdateCvars(ammo); HUD_Panel_ApplyFadeAlpha(); + + draw_beginBoldFont(); + vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -957,35 +965,40 @@ void HUD_Ammo(void) if(autocvar__hud_configure) { DrawAmmoItem(pos, ammo_size, 2, true, FALSE); //show rockets - return; } + else + { + stat_items = getstati(STAT_ITEMS, 0, 24); + if (stat_items & IT_UNLIMITED_WEAPON_AMMO) + infinite_ammo = TRUE; + for (i = 0; i < AMMO_COUNT; ++i) { + currently_selected = stat_items & GetAmmoItemCode(i); + if (currently_selected) + { + DrawAmmoItem(pos, ammo_size, i, true, infinite_ammo); + break; + } + } + } + } + else + { stat_items = getstati(STAT_ITEMS, 0, 24); if (stat_items & IT_UNLIMITED_WEAPON_AMMO) infinite_ammo = TRUE; for (i = 0; i < AMMO_COUNT; ++i) { currently_selected = stat_items & GetAmmoItemCode(i); - if (currently_selected) + DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo); + ++row; + if(row >= rows) { - DrawAmmoItem(pos, ammo_size, i, true, infinite_ammo); - return; + row = 0; + column = column + 1; } } - return; // nothing to display } - stat_items = getstati(STAT_ITEMS, 0, 24); - if (stat_items & IT_UNLIMITED_WEAPON_AMMO) - infinite_ammo = TRUE; - for (i = 0; i < AMMO_COUNT; ++i) { - currently_selected = stat_items & GetAmmoItemCode(i); - DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo); - ++row; - if(row >= rows) - { - row = 0; - column = column + 1; - } - } + draw_endBoldFont(); } void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, float vertical, float icon_right_align, vector color, float theAlpha, float fadelerp) @@ -1106,6 +1119,9 @@ void HUD_Powerups(void) HUD_Panel_UpdateCvars(powerups); HUD_Panel_ApplyFadeAlpha(); + + draw_beginBoldFont(); + vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -1272,6 +1288,8 @@ void HUD_Powerups(void) DrawNumIcon_expanding(pos + superweapons_offset, mySize, superweapons, "superweapons", is_vertical, superweapons_iconalign, '1 1 1', 1, bound(0, (superweapons - superweapons_time) / 0.5, 1)); } } + + draw_endBoldFont(); } // Health/armor (#3) @@ -1744,11 +1762,15 @@ void HUD_KillNotify(string s1, string s2, string s3, float type, float msg) // s } else if(type == DEATH_SBBLOWUP) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) - print (sprintf(_("^1%s^1 got caught in the destruction of %s^1's vehicle\n"), s2, s1)); + print (sprintf(_("^1%s^1 got caught in the blast when %s^1's destroys a vehicle\n"), s2, s1)); } else if(type == DEATH_WAKIGUN) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) print (sprintf(_("^1%s^1 was bolted down by %s\n"), s2, s1)); + } else if(type == DEATH_BUMB_GUN) { + HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); + if(alsoprint) + print (sprintf(_("^1%s^1 saw %s's preddy lights.\n"), s2, s1)); } else if(type == DEATH_WAKIROCKET) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) @@ -1756,7 +1778,7 @@ void HUD_KillNotify(string s1, string s2, string s3, float type, float msg) // s } else if(type == DEATH_WAKIBLOWUP) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) - print (sprintf(_("^1%s^1 dies when %s^1's wakizashi dies.\n"), s2, s1)); + print (sprintf(_("^1%s^1 got caught in the blast when %s^1's destroys a vehicle\n"), s2, s1)); } else if(type == DEATH_RAPTOR_CANNON) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) @@ -1768,7 +1790,7 @@ void HUD_KillNotify(string s1, string s2, string s3, float type, float msg) // s } else if(type == DEATH_RAPTOR_DEATH) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) - print (sprintf(_("^1%s^1 dies when %s^1's raptor dies.\n"), s2, s1)); + print (sprintf(_("^1%s^1 got caught in the blast when %s^1's destroys a vehicle\n"), s2, s1)); } else if(type == DEATH_TURRET) { HUD_KillNotify_Push(s1, s2, 1, DEATH_GENERIC); if(alsoprint) @@ -2435,6 +2457,9 @@ void HUD_Timer(void) HUD_Panel_UpdateCvars(timer); HUD_Panel_ApplyFadeAlpha(); + + draw_beginBoldFont(); + vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -2477,6 +2502,8 @@ void HUD_Timer(void) } drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL); + + draw_endBoldFont(); } // Radar (#6) @@ -2801,6 +2828,7 @@ void HUD_Score(void) score = me.(scores[ps_primary]); timer = TIME_ENCODED_TOSTRING(score); + draw_beginBoldFont(); if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) { // distribution display distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]); @@ -2821,6 +2849,7 @@ void HUD_Score(void) if (distribution <= 0) HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); drawstring_aspect(pos, timer, eX * 0.75 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); } else if (!teamplay) { // non-teamgames if ((spectatee_status == -1 && !autocvar__hud_configure) || autocvar_hud_panel_score_rankings) { @@ -2854,6 +2883,7 @@ void HUD_Score(void) string distribution_str; distribution_str = ftos(distribution); + draw_beginBoldFont(); if (distribution >= 0) { if (distribution > 0) @@ -2862,6 +2892,7 @@ void HUD_Score(void) } drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize_x + eY * mySize_y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL); drawstring_aspect(pos + eX * 0.75 * mySize_x, distribution_str, eX * 0.25 * mySize_x + eY * (1/3) * mySize_y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); } else { // teamgames float scores_count, row, column, rows, columns; local noref vector offset; // fteqcc sucks @@ -2904,6 +2935,7 @@ void HUD_Score(void) float max_fragcount; max_fragcount = -99; + draw_beginBoldFont(); for(tm = teams.sort_next; tm; tm = tm.sort_next) { if(tm.team == COLOR_SPECTATOR) continue; @@ -2938,6 +2970,7 @@ void HUD_Score(void) ++rows; } } + draw_endBoldFont(); } } @@ -2956,6 +2989,9 @@ void HUD_RaceTimer (void) HUD_Panel_UpdateCvars(racetimer); HUD_Panel_ApplyFadeAlpha(); + + draw_beginBoldFont(); + vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -3084,6 +3120,8 @@ void HUD_RaceTimer (void) } } } + + draw_endBoldFont(); } // Vote window (#9) @@ -3906,6 +3944,9 @@ void HUD_ModIcons(void) HUD_Panel_UpdateCvars(modicons); HUD_Panel_ApplyFadeAlpha(); + + draw_beginBoldFont(); + vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -3944,6 +3985,8 @@ void HUD_ModIcons(void) HUD_Mod_Dom(pos, mySize); else if(gametype == MAPINFO_TYPE_KEEPAWAY) HUD_Mod_Keepaway(pos, mySize); + + draw_endBoldFont(); } // Draw pressed keys (#11) @@ -3995,10 +4038,17 @@ void HUD_DrawPressedKeys(void) } vector keysize; - keysize = eX * mySize_x * (1/3) + eY * mySize_y * 0.5; + keysize = eX * mySize_x * (1/3.0) + eY * mySize_y * (1/(3.0 - !autocvar_hud_panel_pressedkeys_attack)); float pressedkeys; pressedkeys = getstatf(STAT_PRESSED_KEYS); + if(autocvar_hud_panel_pressedkeys_attack) + { + drawpic_aspect_skin(pos + eX * keysize_x * 0.5, ((pressedkeys & KEY_ATCK) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos + eX * keysize_x * 1.5, ((pressedkeys & KEY_ATCK2) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + pos_y += keysize_y; + } + drawpic_aspect_skin(pos, ((pressedkeys & KEY_CROUCH) ? "key_crouch_inv.tga" : "key_crouch.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); drawpic_aspect_skin(pos + eX * keysize_x, ((pressedkeys & KEY_FORWARD) ? "key_forward_inv.tga" : "key_forward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); drawpic_aspect_skin(pos + eX * keysize_x * 2, ((pressedkeys & KEY_JUMP) ? "key_jump_inv.tga" : "key_jump.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); @@ -4359,6 +4409,8 @@ void HUD_Physics(void) HUD_Panel_UpdateCvars(physics); HUD_Panel_ApplyFadeAlpha(); + draw_beginBoldFont(); + HUD_Panel_DrawBg(1); if(panel_bg_padding) { @@ -4611,6 +4663,8 @@ void HUD_Physics(void) tmp_offset_y = (panel_size_y - tmp_size_y) / 2; if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3) drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(acceleration, 2), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + draw_endBoldFont(); } // CenterPrint (#16) diff --git a/qcsrc/client/hud_config.qc b/qcsrc/client/hud_config.qc index 7f999ba77d..d1d5dfc8ae 100644 --- a/qcsrc/client/hud_config.qc +++ b/qcsrc/client/hud_config.qc @@ -138,6 +138,7 @@ void HUD_Panel_ExportCfg(string cfgname) HUD_Write_PanelCvar_q("_dom_layout"); break; case HUD_PANEL_PRESSEDKEYS: + HUD_Write_PanelCvar_q("_attack"); HUD_Write_PanelCvar_q("_aspect"); break; case HUD_PANEL_ENGINEINFO: @@ -913,7 +914,7 @@ float HUD_Panel_Check_Mouse_Pos(float allow_move) border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize // move - if(allow_move && mousepos_x >= panel_pos_x && mousepos_y >= panel_pos_y && mousepos_x <= panel_pos_x + panel_size_x && mousepos_y <= panel_pos_y + panel_size_y) + if(allow_move && mousepos_x > panel_pos_x && mousepos_y > panel_pos_y && mousepos_x < panel_pos_x + panel_size_x && mousepos_y < panel_pos_y + panel_size_y) { return 1; } @@ -993,7 +994,7 @@ void HUD_Panel_Highlight(float allow_move) border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize // move - if(allow_move && mousepos_x >= panel_pos_x && mousepos_y >= panel_pos_y && mousepos_x <= panel_pos_x + panel_size_x && mousepos_y <= panel_pos_y + panel_size_y) + if(allow_move && mousepos_x > panel_pos_x && mousepos_y > panel_pos_y && mousepos_x < panel_pos_x + panel_size_x && mousepos_y < panel_pos_y + panel_size_y) { highlightedPanel = i; HUD_Panel_FirstInDrawQ(i); diff --git a/qcsrc/client/mapvoting.qc b/qcsrc/client/mapvoting.qc index cf5236af1a..0961096a4d 100644 --- a/qcsrc/client/mapvoting.qc +++ b/qcsrc/client/mapvoting.qc @@ -189,6 +189,7 @@ void MapVote_Draw() pos_y = ymin; pos_z = 0; + draw_beginBoldFont(); map = _("Vote for a map"); pos_x = center - stringwidth(map, false, '12 0 0'); drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL); @@ -200,6 +201,7 @@ void MapVote_Draw() drawstring(pos, map, '16 16 0', '0 1 0', 1, DRAWFLAG_NORMAL); pos_y += 22; pos_x = xmin; + draw_endBoldFont(); // base for multi-column stuff... ymin = pos_y; diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index 070096fbf4..72bb9c5c40 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -627,3 +627,13 @@ void URI_Get_Callback(float id, float status, string data) print(sprintf(_("Received HTTP request data for an invalid id %d.\n"), id)); } } + +void draw_beginBoldFont() +{ + drawfont = FONT_USER+2; +} + +void draw_endBoldFont() +{ + drawfont = FONT_USER+1; +} diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index d3a3a07d81..0922433eeb 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -53,6 +53,7 @@ projectile.qh sortlist.qc miscfunctions.qc teamplay.qc +../server/t_items.qc teamradar.qc hud_config.qc @@ -79,6 +80,7 @@ tuba.qc target_music.qc vehicles/vehicles.qc +../server/vehicles/bumblebee.qc shownames.qh shownames.qc diff --git a/qcsrc/client/projectile.qc b/qcsrc/client/projectile.qc index 85d5aedcb5..4081bd8d18 100644 --- a/qcsrc/client/projectile.qc +++ b/qcsrc/client/projectile.qc @@ -303,6 +303,10 @@ void Ent_Projectile() case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break; case PROJECTILE_WAKIROCKET: setmodel(self, "models/vehicles/rocket01.md3"); self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break; case PROJECTILE_WAKICANNON: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum(""); break; + + case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + default: error("Received invalid CSQC projectile, can't work with this!"); break; @@ -406,9 +410,21 @@ void Ent_Projectile() break; case PROJECTILE_WAKIROCKET: loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM); - break; + break; + /* case PROJECTILE_WAKICANNON: break; + case PROJECTILE_BUMBLE_GUN: + // only new engines support sound moving with object + loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM); + self.mins = '0 0 -4'; + self.maxs = '0 0 -4'; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = SUB_Null; + self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; + self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; + break; + */ default: break; } diff --git a/qcsrc/client/scoreboard.qc b/qcsrc/client/scoreboard.qc index e828a17d57..364f9e969e 100644 --- a/qcsrc/client/scoreboard.qc +++ b/qcsrc/client/scoreboard.qc @@ -19,6 +19,7 @@ string TranslateScoresLabel(string l) case "bckills": return CTX(_("SCO^bckills")); case "bctime": return CTX(_("SCO^bctime")); case "caps": return CTX(_("SCO^caps")); + case "captime": return CTX(_("SCO^captime")); case "deaths": return CTX(_("SCO^deaths")); case "destroyed": return CTX(_("SCO^destroyed")); case "drops": return CTX(_("SCO^drops")); @@ -254,6 +255,7 @@ void Cmd_HUD_Help() print(_("^3kd^7 The kill-death ratio\n")); print(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n")); print(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n")); + print(_("^3captime^7 Time of fastest cap (CTF)\n")); print(_("^3fckills^7 Number of flag carrier kills\n")); print(_("^3returns^7 Number of flag returns\n")); print(_("^3drops^7 Number of flag drops\n")); @@ -341,6 +343,7 @@ void Cmd_HUD_SetFields(float argc) hud_fontsize = HUD_GetFontsize("hud_fontsize"); + draw_beginBoldFont(); for(i = 0; i < argc - 1; ++i) { float nocomplain; @@ -477,6 +480,7 @@ void Cmd_HUD_SetFields(float argc) } hud_field[hud_num_fields] = SP_END; + draw_endBoldFont(); } // MOVEUP:: @@ -840,6 +844,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz } // print the strings of the columns headers and draw the columns + draw_beginBoldFont(); for(i = 0; i < hud_num_fields; ++i) { if(hud_field[i] == SP_SEPARATOR) @@ -882,6 +887,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz pos_x -= hud_fontsize_x; } } + draw_endBoldFont(); pos_x = xmin; pos_y += 1.25 * hud_fontsize_y; // skip the header @@ -992,7 +998,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) for(i = WEP_FIRST; i <= WEP_LAST; ++i) { self = get_weaponinfo(i); - if not(self.weapons) + if not(self.weapon) continue; if ((i == WEP_NEX && g_minstagib) || i == WEP_PORTO || (i == WEP_MINSTANEX && !g_minstagib) || i == WEP_TUBA) // skip port-o-launch, nex || minstanex and tuba continue; @@ -1220,7 +1226,9 @@ void HUD_DrawScoreboard() // Heading vector sb_heading_fontsize; sb_heading_fontsize = hud_fontsize * 2; + draw_beginBoldFont(); drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); + draw_endBoldFont(); pos_y += sb_heading_fontsize_y + hud_fontsize_y * 0.25; @@ -1236,6 +1244,7 @@ void HUD_DrawScoreboard() if(tm.team == COLOR_SPECTATOR) continue; + draw_beginBoldFont(); rgb = GetTeamRGB(tm.team); str = ftos(tm.(teamscores[ts_primary])); drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL); @@ -1245,6 +1254,8 @@ void HUD_DrawScoreboard() str = ftos(tm.(teamscores[ts_secondary])); drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize) + eY * hud_fontsize_y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL); } + draw_endBoldFont(); + pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size); } } @@ -1302,7 +1313,9 @@ void HUD_DrawScoreboard() if(specs) { + draw_beginBoldFont(); drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); + draw_endBoldFont(); pos_y += 1.25 * hud_fontsize_y; } @@ -1361,9 +1374,30 @@ void HUD_DrawScoreboard() } } - pos_y += 1.2 * hud_fontsize_y; drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL); + // print information about respawn status + float respawn_time = getstatf(STAT_RESPAWN_TIME); + if(respawn_time) + { + if(respawn_time < 0) + { + // a negative number means we are awaiting respawn, time value is still the same + respawn_time *= -1; // remove mark now that we checked it + if(time >= respawn_time) // don't show a negative value while the server is respawning the player (lag) + str = _("^1Respawning..."); + else + str = sprintf(_("^1Respawning in ^3%s^1 seconds..."), ftos_decimals(respawn_time - time, autocvar_scoreboard_respawntime_decimals)); + } + else if(time < respawn_time) + str = sprintf(_("You are dead, wait ^3%s^7 seconds before respawning"), ftos_decimals(respawn_time - time, autocvar_scoreboard_respawntime_decimals)); + else if(time >= respawn_time) + str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump")); + + pos_y += 1.2 * hud_fontsize_y; + drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL); + } + scoreboard_bottom = pos_y + 2 * hud_fontsize_y; } diff --git a/qcsrc/client/tturrets.qc b/qcsrc/client/tturrets.qc index 2b9ec0297b..272a246102 100644 --- a/qcsrc/client/tturrets.qc +++ b/qcsrc/client/tturrets.qc @@ -231,40 +231,54 @@ void turret_draw2d() if(self.netname == "") return; - if(autocvar_cl_hidewaypoints) // also check g_waypointsprites_turrets after next release (needs changed to clients default/config .cfg) + if(!autocvar_g_waypointsprite_turrets) + return; + + if(autocvar_cl_hidewaypoints) return; float dist = vlen(self.origin - view_origin); + float t = (GetPlayerColor(player_localnum) + 1); + vector o; - /* - // TODO: Vehicle tactical hud - o = project_3d_to_2d(self.origin + '0 0 32'); - if(o_z < 0 - || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o_y < (vid_conheight * waypointsprite_edgeoffset_top) - || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) - return; // Dont draw wp's for turrets out of view - o_z = 0; - if(hud != HUD_NORMAL) - { - switch(hud) - { - case HUD_SPIDERBOT: - case HUD_WAKIZASHI: - case HUD_RAPTOR: - vector pz = drawgetimagesize("gfx/vehicles/axh-bracket.tga") * 0.25; - drawpic(o - pz * 0.5 , "gfx/vehicles/axh-bracket.tga", pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); - break; + string txt; + + if(autocvar_cl_vehicles_hud_tactical) + if(dist < 10240 && t != self.team) + { + // TODO: Vehicle tactical hud + o = project_3d_to_2d(self.origin + '0 0 32'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + o_z = 0; + if(hud != HUD_NORMAL) + { + switch(hud) + { + 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 + txt = "gfx/vehicles/vth-stationary.tga"; + + vector pz = drawgetimagesize(txt) * 0.25; + drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); + break; + } } - } - */ + } if(dist > self.maxdistance) return; string spriteimage = self.netname; - float t = (GetPlayerColor(player_localnum) + 1); float a = self.alpha * autocvar_hud_panel_fg_alpha; vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); @@ -280,7 +294,7 @@ void turret_draw2d() print(sprintf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage)); } - string txt = self.netname; + txt = self.netname; if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam) txt = _("Spam"); else @@ -378,7 +392,7 @@ void turret_walker_draw() return; fixedmakevectors(self.angles); - movelib_groundalign4point(300, 100, 0.25); + movelib_groundalign4point(300, 100, 0.25, 45); setorigin(self, self.origin + self.velocity * dt); self.tur_head.angles += dt * self.tur_head.move_avelocity; self.angles_y = self.move_angles_y; @@ -443,7 +457,7 @@ void turret_construct() self.tur_head.drawmask = MASK_NORMAL; self.anim_start_time = 0; self.draw2d = turret_draw2d; - self.maxdistance = 4000; // use g_waypointsprites_turrets_maxdist after next release (needs changed to cleint's default) + self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; self.teamradar_color = '1 0 0'; self.alpha = 1; @@ -540,37 +554,38 @@ entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, flo void turret_die() { - entity headgib; sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); turret_tid2info(self.turret_type); - - // Base - if(self.turret_type == TID_EWHEEL) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); - else if (self.turret_type == TID_WALKER) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); - else if (self.turret_type == TID_TESLA) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); - else - { - if (random() > 0.5) - { - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - } + if (!autocvar_cl_nogibs) + { + // Base + if(self.turret_type == TID_EWHEEL) + turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); + else if (self.turret_type == TID_WALKER) + turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); + else if (self.turret_type == TID_TESLA) + turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); else - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); - - headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); - if(headgib) - { - headgib.angles = headgib.move_angles = self.tur_head.angles; - headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45; - headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; - headgib.gravity = 0.5; + { + if (random() > 0.5) + { + turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); + turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); + turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); + } + else + turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); + + entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); + if(headgib) + { + headgib.angles = headgib.move_angles = self.tur_head.angles; + headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45; + headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; + headgib.gravity = 0.5; + } } } diff --git a/qcsrc/client/tuba.qc b/qcsrc/client/tuba.qc index 2fea077c5d..a22347fe3c 100644 --- a/qcsrc/client/tuba.qc +++ b/qcsrc/client/tuba.qc @@ -1,6 +1,6 @@ #define TUBA_MIN -18 #define TUBA_MAX 27 -#define TUBA_INSTRUMENTS 2 +#define TUBA_INSTRUMENTS 3 #define TUBA_STARTNOTE(i,n) strcat("weapons/tuba", (i ? ftos(i) : ""), "_loopnote", ftos(n), ".wav") .float note; // note diff --git a/qcsrc/client/vehicles/vehicles.qc b/qcsrc/client/vehicles/vehicles.qc index ccb9cff583..3a4597ab2e 100644 --- a/qcsrc/client/vehicles/vehicles.qc +++ b/qcsrc/client/vehicles/vehicles.qc @@ -10,16 +10,34 @@ #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 SBRM_GUIDE 2 +#define SBRM_ARTILLERY 3 +#define SBRM_LAST 3 + +#define RSM_FIRST 1 +#define RSM_BOMB 1 +#define RSM_FLARE 2 +#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" +#define raptor_bomb "gfx/vehicles/raptor_bombs.tga" +#define raptor_drop "gfx/vehicles/axh-dropcross.tga" +string raptor_xhair; void CSQC_WAKIZASHI_HUD(); void CSQC_SPIDER_HUD(); void CSQC_RAPTOR_HUD(); void CSQC_BUMBLE_HUD(); +void CSQC_BUMBLE_GUN_HUD(); #define MAX_AXH 4 entity AuxiliaryXhair[MAX_AXH]; @@ -30,12 +48,34 @@ const var void Draw_Not(); .float axh_drawflag; .float axh_scale; +#define bumb_ico "gfx/vehicles/bumb.tga" +#define bumb_lgun "gfx/vehicles/bumb_lgun.tga" +#define bumb_rgun "gfx/vehicles/bumb_rgun.tga" + +#define bumb_gun_ico "gfx/vehicles/bumb_side.tga" +#define bumb_gun_gun "gfx/vehicles/bumb_side_gun.tga" + +#define spider_ico "gfx/vehicles/sbot.tga" +#define spider_rkt "gfx/vehicles/sbot_rpods.tga" +#define spider_mgun "gfx/vehicles/sbot_mguns.tga" +string spider_xhair; // = "gfx/vehicles/axh-special1.tga"; + +#define waki_ico "gfx/vehicles/waki.tga" +#define waki_eng "gfx/vehicles/waki_e.tga" +#define waki_gun "gfx/vehicles/waki_guns.tga" +#define waki_rkt "gfx/vehicles/waki_rockets.tga" +#define waki_xhair "gfx/vehicles/axh-special1.tga" + +float alarm1time; +float alarm2time; +float weapon2mode; + void AuxiliaryXhair_Draw2D() { vector loc, psize; psize = self.axh_scale * draw_getimagesize(self.axh_image); - loc = project_3d_to_2d(self.origin) - 0.5 * psize; + loc = project_3d_to_2d(self.move_origin) - 0.5 * psize; if not (loc_z < 0 || loc_x < 0 || loc_y < 0 || loc_x > vid_conwidth || loc_y > vid_conheight) { loc_z = 0; @@ -49,54 +89,55 @@ void AuxiliaryXhair_Draw2D() void Net_AuXair2(float bIsNew) { - float axh_id; - entity axh; - - axh_id = bound(0, ReadByte(), MAX_AXH); - axh = AuxiliaryXhair[axh_id]; + float axh_id = bound(0, ReadByte(), MAX_AXH); + entity axh = AuxiliaryXhair[axh_id]; if(axh == world || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) { - axh = spawn(); - axh.draw2d = Draw_Not; - axh.drawmask = MASK_NORMAL; - axh.axh_drawflag = DRAWFLAG_ADDITIVE; - axh.axh_fadetime = 0.1; - axh.axh_image = "gfx/vehicles/axh-ring.tga"; - axh.axh_scale = 1; - axh.alpha = 1; + axh = spawn(); + axh.draw2d = Draw_Not; + axh.drawmask = MASK_NORMAL; + axh.axh_drawflag = DRAWFLAG_ADDITIVE; + axh.axh_fadetime = 0.1; + axh.axh_image = "gfx/vehicles/axh-ring.tga"; + axh.axh_scale = 1; + axh.alpha = 1; AuxiliaryXhair[axh_id] = axh; } - - axh.draw2d = AuxiliaryXhair_Draw2D; - - axh.origin_x = ReadCoord(); - axh.origin_y = ReadCoord(); - axh.origin_z = ReadCoord(); - - axh.colormod_x = ReadByte() / 255; - axh.colormod_y = ReadByte() / 255; - axh.colormod_z = ReadByte() / 255; - axh.cnt = time; + + axh.move_origin_x = ReadCoord(); + axh.move_origin_y = ReadCoord(); + axh.move_origin_z = ReadCoord(); + axh.colormod_x = ReadByte() / 255; + axh.colormod_y = ReadByte() / 255; + axh.colormod_z = ReadByte() / 255; + axh.cnt = time; + axh.draw2d = AuxiliaryXhair_Draw2D; } void Net_VehicleSetup() { - float hud_id, i; + float i; - hud_id = ReadByte(); + float hud_id = ReadByte(); + + // Weapon update? + if(hud_id > HUD_VEHICLE_LAST) + { + weapon2mode = hud_id - HUD_VEHICLE_LAST; + return; + } // hud_id == 0 means we exited a vehicle, so stop alarm sound/s if(hud_id == 0) { 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; @@ -106,15 +147,15 @@ void Net_VehicleSetup() if(axh != world && !wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) remove(axh); - axh = spawn(); - axh.draw2d = Draw_Not; - axh.drawmask = MASK_NORMAL; - axh.axh_drawflag = DRAWFLAG_NORMAL; - axh.axh_fadetime = 0.1; - axh.axh_image = "gfx/vehicles/axh-ring.tga"; - axh.axh_scale = 1; - axh.alpha = 1; - AuxiliaryXhair[i] = axh; + axh = spawn(); + axh.draw2d = Draw_Not; + axh.drawmask = MASK_NORMAL; + axh.axh_drawflag = DRAWFLAG_NORMAL; + axh.axh_fadetime = 0.1; + axh.axh_image = "gfx/vehicles/axh-ring.tga"; + axh.axh_scale = 1; + axh.alpha = 1; + AuxiliaryXhair[i] = axh; } switch(hud_id) @@ -148,11 +189,24 @@ void Net_VehicleSetup() break; case HUD_BUMBLEBEE: + // Raygun-locked + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhair[0].axh_scale = 0.5; + + // Gunner1 + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-target.tga"; + AuxiliaryXhair[1].axh_scale = 0.75; + + // Gunner2 + AuxiliaryXhair[2].axh_image = "gfx/vehicles/axh-target.tga"; + AuxiliaryXhair[2].axh_scale = 0.75; + break; + case HUD_BUMBLEBEE_GUN: // Plasma cannons - AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga"; AuxiliaryXhair[0].axh_scale = 0.25; // Raygun - AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-special1.tga"; + AuxiliaryXhair[1].axh_image = "gfx/vehicles/axh-bracket.tga"; AuxiliaryXhair[1].axh_scale = 0.25; break; } @@ -168,15 +222,281 @@ void Net_VehicleSetup() void CSQC_BUMBLE_HUD() { +/* + drawpic(hudloc, waki_s, picsize, '1 1 1', shield, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_b, picsize, '0 1 0' * health + '1 0 0' * (1 - health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_r, picsize, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_e, picsize, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); +*/ + 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_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + + if(vh_health < 0.25) + drawpic(hudloc + picloc, bumb_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + 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 + 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; + } + } + + 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 * 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 = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + 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_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, 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 + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + 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_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); + 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); + } + */ } -#define spider_ico "gfx/vehicles/sbot.tga" -#define spider_rkt "gfx/vehicles/sbot_rpods.tga" -#define spider_mgun "gfx/vehicles/sbot_mguns.tga" -#define spider_xhair "gfx/vehicles/axh-special1.tga" -float alarm1time; -float alarm2time; void CSQC_SPIDER_HUD() { @@ -195,11 +515,6 @@ void CSQC_SPIDER_HUD() drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); - //drawstring(hudloc + '145 19 0', strcat(ftos(vh_health), "%"),'15 15 0','0 1 0', 1, DRAWFLAG_NORMAL); - //drawstring(hudloc + '175 34 0', strcat(ftos(shield), "%"),'15 15 0','0 0 1', 1, DRAWFLAG_NORMAL); - //drawstring(hudloc + '136 102 0', strcat(ftos(ammo1), "%"),'14 14 0','1 1 0', 1, DRAWFLAG_NORMAL); - //drawstring(hudloc + '179 69 0', strcat(ftos(9 - ammo2), " / 8"),'14 14 0','1 1 0', 1, DRAWFLAG_NORMAL); - ammo1 *= 0.01; shield *= 0.01; vh_health *= 0.01; @@ -269,7 +584,6 @@ void CSQC_SPIDER_HUD() alarm2time = 0; } } - // Minigun bar picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; @@ -324,6 +638,21 @@ void CSQC_SPIDER_HUD() HUD_DrawScoreboard(); else { + switch(weapon2mode) + { + case SBRM_VOLLY: + spider_xhair = "gfx/vehicles/axh-bracket.tga"; + break; + case SBRM_GUIDE: + spider_xhair = "gfx/vehicles/axh-cross.tga"; + break; + case SBRM_ARTILLERY: + spider_xhair = "gfx/vehicles/axh-tag.tga"; + break; + default: + spider_xhair= "gfx/vehicles/axh-tag.tga"; + } + picsize = draw_getimagesize(spider_xhair); picsize_x *= autocvar_cl_vehicle_spiderbot_cross_size; picsize_y *= autocvar_cl_vehicle_spiderbot_cross_size; @@ -332,11 +661,6 @@ void CSQC_SPIDER_HUD() } } -#define raptor_ico "gfx/vehicles/raptor.tga" -#define raptor_gun "gfx/vehicles/raptor_guns.tga" -#define raptor_bomb "gfx/vehicles/raptor_bombs.tga" -#define raptor_drop "gfx/vehicles/axh-dropcross.tga" -#define raptor_xhair "gfx/vehicles/axh-ring.tga" void CSQC_RAPTOR_HUD() { if(autocvar_r_letterbox) @@ -455,55 +779,64 @@ void CSQC_RAPTOR_HUD() drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '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); - -// Bombing crosshair - if(!dropmark) + + if(weapon2mode == RSM_FLARE) { - dropmark = spawn(); - dropmark.owner = self; - dropmark.gravity = 1; + raptor_xhair = "gfx/vehicles/axh-bracket.tga"; } - - if(reload2 == 100) + else { - vector where; + raptor_xhair = "gfx/vehicles/axh-ring.tga"; + + // Bombing crosshair + if(!dropmark) + { + dropmark = spawn(); + dropmark.owner = self; + dropmark.gravity = 1; + } - setorigin(dropmark, pmove_org); - dropmark.velocity = pmove_vel; - tracetoss(dropmark, self); + if(reload2 == 100) + { + vector where; - where = project_3d_to_2d(trace_endpos); + setorigin(dropmark, pmove_org); + dropmark.velocity = pmove_vel; + tracetoss(dropmark, self); - setorigin(dropmark, trace_endpos); - picsize = draw_getimagesize(raptor_drop) * 0.2; + where = project_3d_to_2d(trace_endpos); - if not (where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight) - { - where_x -= picsize_x * 0.5; - where_y -= picsize_y * 0.5; - where_z = 0; - drawpic(where, raptor_drop, picsize, '0 2 0', 1, DRAWFLAG_ADDITIVE); - } - dropmark.cnt = time + 5; - } - else - { - vector where; - if(dropmark.cnt > time) - { - where = project_3d_to_2d(dropmark.origin); - picsize = draw_getimagesize(raptor_drop) * 0.25; + setorigin(dropmark, trace_endpos); + picsize = draw_getimagesize(raptor_drop) * 0.2; if not (where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight) { where_x -= picsize_x * 0.5; where_y -= picsize_y * 0.5; where_z = 0; - drawpic(where, raptor_drop, picsize, '2 0 0', 1, DRAWFLAG_ADDITIVE); + drawpic(where, raptor_drop, picsize, '0 2 0', 1, DRAWFLAG_ADDITIVE); + } + dropmark.cnt = time + 5; + } + else + { + vector where; + if(dropmark.cnt > time) + { + where = project_3d_to_2d(dropmark.origin); + picsize = draw_getimagesize(raptor_drop) * 0.25; + + if not (where_z < 0 || where_x < 0 || where_y < 0 || where_x > vid_conwidth || where_y > vid_conheight) + { + where_x -= picsize_x * 0.5; + where_y -= picsize_y * 0.5; + where_z = 0; + drawpic(where, raptor_drop, picsize, '2 0 0', 1, DRAWFLAG_ADDITIVE); + } } } } - + if (scoreboard_showscores) HUD_DrawScoreboard(); else @@ -516,11 +849,6 @@ void CSQC_RAPTOR_HUD() } } -#define waki_ico "gfx/vehicles/waki.tga" -#define waki_eng "gfx/vehicles/waki_e.tga" -#define waki_gun "gfx/vehicles/waki_guns.tga" -#define waki_rkt "gfx/vehicles/waki_rockets.tga" -#define waki_xhair "gfx/vehicles/axh-special1.tga" void CSQC_WAKIZASHI_HUD() { /* @@ -660,13 +988,6 @@ void CSQC_WAKIZASHI_HUD() void Vehicles_Precache() { -// fixme: HAAAAKKKZZZ!!!!!!!!!!!! (this belongs as a setting in default.cfg) - autocvar_cl_vehicles_hudscale = 0.5; - 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"); @@ -679,15 +1000,15 @@ void Vehicles_Precache() void RaptorCBShellfragDraw() { - - Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); if(wasfreed(self)) - return; - + return; + + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); self.move_avelocity += randomvec() * 15; self.renderflags = 0; + if(self.cnt < time) - self.alpha = bound(0, self.nextthink - time, 1); + self.alpha = bound(0, self.nextthink - time, 1); if(self.alpha < ALPHA_MIN_VISIBLE) remove(self); diff --git a/qcsrc/client/waypointsprites.qc b/qcsrc/client/waypointsprites.qc index 78ddc8b793..d3d57aba9e 100644 --- a/qcsrc/client/waypointsprites.qc +++ b/qcsrc/client/waypointsprites.qc @@ -256,6 +256,7 @@ string spritelookuptext(string s) case "as-defend": return _("Defend"); case "bluebase": return _("Blue base"); case "danger": return _("DANGER"); + case "enemyflagcarrier": return _("Enemy carrier"); case "flagcarrier": return _("Flag carrier"); case "flagdropped": return _("Dropped flag"); case "helpme": return _("Help me!"); @@ -595,6 +596,7 @@ void Draw_WaypointSprite() if(autocvar_g_waypointsprite_uppercase) txt = strtoupper(txt); + draw_beginBoldFont(); if(self.health >= 0) { o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); @@ -630,6 +632,7 @@ void Draw_WaypointSprite() { o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt); } + draw_endBoldFont(); } void Ent_RemoveWaypointSprite() diff --git a/qcsrc/common/campaign_file.qc b/qcsrc/common/campaign_file.qc index 067cd8054d..5ab3da67cf 100644 --- a/qcsrc/common/campaign_file.qc +++ b/qcsrc/common/campaign_file.qc @@ -38,13 +38,12 @@ float CampaignFile_Load(float offset, float n) { entlen = tokenize(l); // using insane tokenizer for CSV -#define CAMPAIGN_GETARG0 if(i >= entlen) -#define CAMPAIGN_GETARG1 CAMPAIGN_GETARG0 error("syntax error in campaign file: line has not enough fields"); -#define CAMPAIGN_GETARG2 CAMPAIGN_GETARG1 a = argv(++i); -#define CAMPAIGN_GETARG3 CAMPAIGN_GETARG2 if(a == ",") -#define CAMPAIGN_GETARG4 CAMPAIGN_GETARG3 a = ""; -#define CAMPAIGN_GETARG5 CAMPAIGN_GETARG4 else -#define CAMPAIGN_GETARG CAMPAIGN_GETARG5 ++i +#define CAMPAIGN_GETARG \ + a = argv(++i); \ + if(a == ",") \ + a = ""; \ + else \ + ++i // What you're seeing here is what people will do when your compiler supports // C-style macros but no line continuations. @@ -58,6 +57,10 @@ float CampaignFile_Load(float offset, float n) CAMPAIGN_GETARG; campaign_mutators[campaign_entries] = strzone(a); CAMPAIGN_GETARG; campaign_shortdesc[campaign_entries] = strzone(a); CAMPAIGN_GETARG; campaign_longdesc[campaign_entries] = strzone(strreplace("\\n", "\n", a)); + + if(i > entlen) + error("syntax error in campaign file: line has not enough fields"); + campaign_entries = campaign_entries + 1; if(campaign_entries >= n) diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 73fe45c406..b7b8da0e9f 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -263,6 +263,7 @@ void GenericCommand_maplist(float request, float argc) MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); argc = tokenizebyseparator(cvar_string("g_maplist"), " "); + tmp_string = ""; for(i = 0; i < argc; ++i) if(MapInfo_CheckMap(argv(i))) tmp_string = strcat(tmp_string, " ", argv(i)); @@ -279,6 +280,7 @@ void GenericCommand_maplist(float request, float argc) { argc = tokenizebyseparator(cvar_string("g_maplist"), " "); + tmp_string = ""; for(i = 0; i < argc; ++i) if(argv(i) != argv(2)) tmp_string = strcat(tmp_string, " ", argv(i)); @@ -349,6 +351,7 @@ void GenericCommand_removefromlist(float request, float argc) argc = tokenizebyseparator(cvar_string(original_cvar), " "); + tmp_string = ""; for(i = 0; i < argc; ++i) if(argv(i) != removal) tmp_string = strcat(tmp_string, " ", argv(i)); diff --git a/qcsrc/common/command/markup.qc b/qcsrc/common/command/markup.qc index cb1a8cc371..d1562aaac4 100644 --- a/qcsrc/common/command/markup.qc +++ b/qcsrc/common/command/markup.qc @@ -51,6 +51,7 @@ void GenericCommand_markup_init() markup_from[i] = "&.."; markup_to[i] = "\x9e"; ++i; markup_from[i] = "&.)"; markup_to[i] = "\x9f"; ++i; markup_from[i] = "&<|"; markup_to[i] = "\xff"; ++i; + unused_float = i; } string GenericCommand_markup(string s2) diff --git a/qcsrc/common/command/rpn.qc b/qcsrc/common/command/rpn.qc index c15a8d26ef..5cca2db1ce 100644 --- a/qcsrc/common/command/rpn.qc +++ b/qcsrc/common/command/rpn.qc @@ -236,9 +236,9 @@ void GenericCommand_rpn(float request, float argc, string command) i = stof(db_get(rpn_db, "stack.pointer")); db_put(rpn_db, "stack.pointer", ftos(i+1)); db_put(rpn_db, strcat("stack.", ftos(i)), s); + if(!i) + db_put(rpn_db, "stack.pos", "0"); } - if(!i) - db_put(rpn_db, "stack.pos", "0"); } else if(rpncmd == "dbpop") { i = stof(db_get(rpn_db, "stack.pointer")); if(i) @@ -556,4 +556,4 @@ void GenericCommand_rpn(float request, float argc, string command) return; } } -} \ No newline at end of file +} diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index e70a83c391..75f5a0bde7 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -97,6 +97,8 @@ const float ENT_CLIENT_ACCURACY = 30; const float ENT_CLIENT_SHOWNAMES = 31; const float ENT_CLIENT_WARPZONE_TELEPORTED = 32; const float ENT_CLIENT_MODEL = 33; +const float ENT_CLIENT_ITEM = 34; +const float ENT_CLIENT_BUMBLE_RAYGUN = 35; const float ENT_CLIENT_TURRET = 40; const float ENT_CLIENT_AUXILIARYXHAIR = 50; @@ -127,6 +129,8 @@ const float KEY_LEFT = 4; const float KEY_RIGHT = 8; const float KEY_JUMP = 16; const float KEY_CROUCH = 32; +const float KEY_ATCK = 64; +const float KEY_ATCK2 = 128; /////////////////////////// // cvar constants @@ -164,28 +168,6 @@ const float STAT_HAGAR_LOAD = 57; const float STAT_SWITCHINGWEAPON = 58; const float STAT_SUPERWEAPONS_FINISHED = 59; -// see DP source, quakedef.h -const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222; -const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223; -const float STAT_MOVEVARS_MAXSPEED = 244; -const float STAT_MOVEVARS_AIRACCEL_QW = 254; - -const float CTF_STATE_ATTACK = 1; -const float CTF_STATE_DEFEND = 2; -const float CTF_STATE_COMMANDER = 3; - -const float HUD_NORMAL = 0; -const float HUD_VEHICLE_FIRST = 10; -const float HUD_SPIDERBOT = 10; -const float HUD_WAKIZASHI = 11; -const float HUD_RAPTOR = 12; -const float HUD_BUMBLEBEE = 13; -const float HUD_VEHICLE_LAST = 13; - -const vector eX = '1 0 0'; -const vector eY = '0 1 0'; -const vector eZ = '0 0 1'; - const float STAT_VEHICLESTAT_HEALTH = 60; const float STAT_VEHICLESTAT_SHIELD = 61; const float STAT_VEHICLESTAT_ENERGY = 62; @@ -197,6 +179,8 @@ const float STAT_VEHICLESTAT_RELOAD2 = 66; const float STAT_SECRETS_TOTAL = 70; const float STAT_SECRETS_FOUND = 71; +const float STAT_RESPAWN_TIME = 72; + // mod stats (1xx) const float STAT_REDALIVE = 100; const float STAT_BLUEALIVE = 101; @@ -207,7 +191,7 @@ const float STAT_PINKALIVE = 103; const float STAT_FROZEN = 104; const float STAT_REVIVE_PROGRESS = 105; - +// domination const float STAT_DOM_TOTAL_PPS = 100; const float STAT_DOM_PPS_RED = 101; const float STAT_DOM_PPS_BLUE = 102; @@ -217,6 +201,29 @@ const float STAT_DOM_PPS_YELLOW = 104; //const float STAT_SPIDERBOT_AIM 53 // compressShotOrigin //const float STAT_SPIDERBOT_TARGET 54 // compressShotOrigin +// see DP source, quakedef.h +const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222; +const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223; +const float STAT_MOVEVARS_MAXSPEED = 244; +const float STAT_MOVEVARS_AIRACCEL_QW = 254; + +const float CTF_STATE_ATTACK = 1; +const float CTF_STATE_DEFEND = 2; +const float CTF_STATE_COMMANDER = 3; + +const float HUD_NORMAL = 0; +const float HUD_VEHICLE_FIRST = 10; +const float HUD_SPIDERBOT = 10; +const float HUD_WAKIZASHI = 11; +const float HUD_RAPTOR = 12; +const float HUD_BUMBLEBEE = 13; +const float HUD_BUMBLEBEE_GUN = 14; +const float HUD_VEHICLE_LAST = 14; + +const vector eX = '1 0 0'; +const vector eY = '0 1 0'; +const vector eZ = '0 0 1'; + // moved that here so the client knows the max. // # of maps, I'll use arrays for them :P #define MAPVOTE_COUNT 10 @@ -346,6 +353,9 @@ float PROJECTILE_SPIDERROCKET = 27; float PROJECTILE_WAKIROCKET = 28; float PROJECTILE_WAKICANNON = 29; +float PROJECTILE_BUMBLE_GUN = 30; +float PROJECTILE_BUMBLE_BEAM = 31; + float SPECIES_HUMAN = 0; float SPECIES_ROBOT_SOLID = 1; float SPECIES_ALIEN = 2; @@ -388,8 +398,12 @@ float DEATH_WAKIBLOWUP = 10036; float DEATH_RAPTOR_CANNON = 10037; float DEATH_RAPTOR_BOMB = 10038; float DEATH_RAPTOR_BOMB_SPLIT = 10039; -float DEATH_RAPTOR_DEATH = 10040; -float DEATH_VHLAST = 10040; +float DEATH_RAPTOR_DEATH = 10040; +float DEATH_BUMB_GUN = 10041; +float DEATH_BUMB_RAY = 10042; +float DEATH_BUMB_RAY_HEAL = 10043; +float DEATH_BUMB_DEATH = 10044; +float DEATH_VHLAST = 10044; #define DEATH_ISVEHICLE(t) ((t) >= DEATH_VHFIRST && (t) <= DEATH_VHLAST) float DEATH_GENERIC = 10050; diff --git a/qcsrc/common/items.qc b/qcsrc/common/items.qc index 3ddf8099f5..0aebde0af2 100644 --- a/qcsrc/common/items.qc +++ b/qcsrc/common/items.qc @@ -8,7 +8,7 @@ void register_weapon(float id, float(float) func, float ammotype, float i, float weapon_info[id - 1] = e = spawn(); e.classname = "weapon_info"; e.weapon = id; - e.weapons = power2of(id - WEP_FIRST); + WEPSET_COPY_EW(e, id); e.netname = shortname; e.message = wname; e.items = ammotype; @@ -41,9 +41,9 @@ void register_weapons_done() dummy_weapon_info = spawn(); dummy_weapon_info.classname = "weapon_info"; dummy_weapon_info.weapon = 0; // you can recognize dummies by this - dummy_weapon_info.weapons = 0; // you can recognize dummies by this too + WEPSET_CLEAR_E(dummy_weapon_info); dummy_weapon_info.netname = ""; - dummy_weapon_info.message = "@!#%'n Tuba"; + dummy_weapon_info.message = "AOL CD Thrower"; dummy_weapon_info.items = 0; dummy_weapon_info.weapon_func = w_null; dummy_weapon_info.mdl = ""; @@ -154,3 +154,21 @@ string W_FixWeaponOrder_ForceComplete(string order) return W_FixWeaponOrder(order, 1); } +void W_RandomWeapons(entity e, float n) +{ + float i, j; + WEPSET_DECLARE_A(remaining); + WEPSET_DECLARE_A(result); + WEPSET_COPY_AE(remaining, e); + WEPSET_CLEAR_A(result); + for(i = 0; i < n; ++i) + { + RandomSelection_Init(); + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + if(WEPSET_CONTAINS_AW(remaining, j)) + RandomSelection_Add(world, j, string_null, 1, 1); + WEPSET_OR_AW(result, RandomSelection_chosen_float); + WEPSET_ANDNOT_AW(remaining, RandomSelection_chosen_float); + } + WEPSET_COPY_EA(e, result); +} diff --git a/qcsrc/common/items.qh b/qcsrc/common/items.qh index 07a7625345..c20715d585 100644 --- a/qcsrc/common/items.qh +++ b/qcsrc/common/items.qh @@ -2,15 +2,16 @@ float BOT_PICKUP_RATING_LOW = 2500; float BOT_PICKUP_RATING_MID = 5000; float BOT_PICKUP_RATING_HIGH = 10000; -float WEP_TYPE_OTHER = 0x00; // e.g: Hook, Port-o-launch, etc -float WEP_TYPE_SPLASH = 0x01; -float WEP_TYPE_HITSCAN = 0x02; -float WEP_TYPEMASK = 0x0F; -float WEP_FLAG_CANCLIMB = 0x10; -float WEP_FLAG_NORMAL = 0x20; -float WEP_FLAG_HIDDEN = 0x40; -float WEP_FLAG_RELOADABLE = 0x80; -float WEP_FLAG_SUPERWEAPON = 0x100; +float WEP_TYPE_OTHER = 0x00; // not for damaging people +float WEP_TYPE_SPLASH = 0x01; // splash damage +float WEP_TYPE_HITSCAN = 0x02; // hitscan +float WEP_TYPEMASK = 0x0F; +float WEP_FLAG_CANCLIMB = 0x10; // can be used for movement +float WEP_FLAG_NORMAL = 0x20; // in "most weapons" set +float WEP_FLAG_HIDDEN = 0x40; // hides from menu +float WEP_FLAG_RELOADABLE = 0x80; // can has reload +float WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer +float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag) float IT_UNLIMITED_WEAPON_AMMO = 1; // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup. @@ -30,16 +31,16 @@ float IT_STRENGTH = 8192; float IT_INVINCIBLE = 16384; float IT_HEALTH = 32768; // union: - // for items: - float IT_KEY1 = 131072; - float IT_KEY2 = 262144; - // for players: - float IT_RED_FLAG_TAKEN = 32768; - float IT_RED_FLAG_LOST = 65536; - float IT_RED_FLAG_CARRING = 98304; - float IT_BLUE_FLAG_TAKEN = 131072; - float IT_BLUE_FLAG_LOST = 262144; - float IT_BLUE_FLAG_CARRING = 393216; + // for items: + float IT_KEY1 = 131072; + float IT_KEY2 = 262144; + // for players: + float IT_RED_FLAG_TAKEN = 32768; + float IT_RED_FLAG_LOST = 65536; + float IT_RED_FLAG_CARRYING = 98304; + float IT_BLUE_FLAG_TAKEN = 131072; + float IT_BLUE_FLAG_LOST = 262144; + float IT_BLUE_FLAG_CARRYING = 393216; // end float IT_5HP = 524288; float IT_25HP = 1048576; @@ -71,7 +72,6 @@ string W_NumberWeaponOrder(string order); // entity properties of weaponinfo: .float weapon; // WEP_... -.float weapons; // WEPBIT_... .string netname; // short name .string message; // human readable name .float items; // IT_... @@ -83,37 +83,149 @@ string W_NumberWeaponOrder(string order); .float bot_pickupbasevalue; // bot weapon priority .string model2; // wpn- sprite name ..float ammo_field; // main ammo field +// also, weaponinfo ents can act as a WEPSET // dynamic weapon adding float w_null(float dummy); void register_weapon(float id, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname); void register_weapons_done(); +#define WEP_FIRST 1 float WEP_COUNT; -float WEP_FIRST = 1; float WEP_LAST; -#define WEP_MAXCOUNT 24 -float WEPBIT_ALL; -float WEPBIT_SUPERWEAPONS; -#define REGISTER_WEAPON_2(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ + +#if 1 +# define WEP_MAXCOUNT 24 +// default storage +.float _WS_weapons; +# define WEPSET_BIT(a) power2of((a) - WEP_FIRST) +# define WEPSET_DECLARE_A(a) float _WS_##a +# define WEPSET_CLEAR_E(e) ((e)._WS_weapons = 0) +# define WEPSET_CLEAR_A(a) (_WS_##a = 0) +# define WEPSET_EMPTY_E(e) ((e)._WS_weapons == 0) +# define WEPSET_EMPTY_A(a) (_WS_##a == 0) +# define WEPSET_COPY_AS(a) (_WS_##a = getstati(STAT_WEAPONS)) +# define WEPSET_ADDSTAT() addstat(STAT_WEAPONS, AS_INT, _WS_weapons) +# define WEPSET_WRITE_E(dest,a) WriteInt24_t(dest, (a)._WS_weapons) +# define WEPSET_WRITE_A(dest,a) WriteInt24_t(dest, _WS_##a) +# define WEPSET_WRITE_W(dest,a) WriteInt24_t(dest, WEPSET_BIT(a)) +# define WEPSET_READ_E(a) (a)._WS_weapons = ReadInt24_t() +# define WEPSET_READ_A(a) (_WS_##a) = ReadInt24_t() +# define WEPSET_OP1_EE(a,b,mergeop,x) ((a)._WS_weapons x (b)._WS_weapons) +# define WEPSET_OP2_EE(a,b,mergeop,x,y) ((a)._WS_weapons x (b)._WS_weapons y (a)._WS_weapons) +# define WEPSET_OP1_EA(a,b,mergeop,x) ((a)._WS_weapons x _WS_##b) +# define WEPSET_OP2_EA(a,b,mergeop,x,y) ((a)._WS_weapons x _WS_##b y (a)._WS_weapons) +# define WEPSET_OP1_EW(a,b,mergeop,x) ((a)._WS_weapons x WEPSET_BIT(b)) +# define WEPSET_OP2_EW(a,b,mergeop,x,y) ((a)._WS_weapons x WEPSET_BIT(b) y (a)._WS_weapons) +# define WEPSET_OP1_AE(a,b,mergeop,x) (_WS_##a x (b)._WS_weapons) +# define WEPSET_OP2_AE(a,b,mergeop,x,y) (_WS_##a x (b)._WS_weapons y _WS_##a) +# define WEPSET_OP1_AA(a,b,mergeop,x) (_WS_##a x _WS_##b) +# define WEPSET_OP2_AA(a,b,mergeop,x,y) (_WS_##a x _WS_##b y _WS_##a) +# define WEPSET_OP1_AW(a,b,mergeop,x) (_WS_##a x WEPSET_BIT(b)) +# define WEPSET_OP2_AW(a,b,mergeop,x,y) (_WS_##a x WEPSET_BIT(b) y _WS_##a) +#else +# define WEP_MAXCOUNT 48 +# define WEP_FIRST2 25 +.float _WS1_weapons; +.float _WS2_weapons; +# define WEPSET_BIT1(a) (((a) < WEP_FIRST2) ? power2of((a) - WEP_FIRST) : 0) +# define WEPSET_BIT2(a) (((a) >= WEP_FIRST2) ? power2of((a) - WEP_FIRST2) : 0) +# define WEPSET_DECLARE_A(a) float _WS1_##a, _WS2_##a +# define WEPSET_CLEAR_E(e) ((e)._WS1_weapons = (e)._WS2_weapons = 0) +# define WEPSET_CLEAR_A(a) ((_WS1_##a) = (_WS2_##a) = 0) +# define WEPSET_EMPTY_E(e) ((e)._WS1_weapons == 0 && (e)._WS2_weapons == 0) +# define WEPSET_EMPTY_A(a) ((_WS1_##a) == 0 && (_WS2_##a) == 0) +# define WEPSET_COPY_AS(a) ((_WS1_##a) = getstati(STAT_WEAPONS), (_WS2_##a) = getstati(STAT_WEAPONS2)) +# define WEPSET_ADDSTAT() addstat(STAT_WEAPONS, AS_INT, _WS1_weapons); addstat(STAT_WEAPONS2, AS_INT, _WS2_weapons) +# define WEPSET_WRITE_E(dest,a) WriteInt24_t(dest, (a)._WS1_weapons); WriteInt24_t(dest, (a)._WS2_weapons) +# define WEPSET_WRITE_A(dest,a) WriteInt24_t(dest, _WS1_##a); WriteInt24_t(dest, _WS2_##a) +# define WEPSET_WRITE_W(dest,a) WriteInt24_t(dest, WEPSET_BIT1(a)); WriteInt24_t(dest, WEPSET_BIT2(a)) +# define WEPSET_READ_E(a) (a)._WS1_weapons = ReadInt24_t(); (a)._WS2_weapons = ReadInt24_t() +# define WEPSET_READ_A(a) (_WS1_##a) = ReadInt24_t(); (_WS2_##a) = ReadInt24_t() +# define WEPSET_OP1_EE(a,b,mergeop,x) (((a)._WS1_weapons x (b)._WS1_weapons) mergeop ((a)._WS2_weapons x (b)._WS2_weapons)) +# define WEPSET_OP2_EE(a,b,mergeop,x,y) (((a)._WS1_weapons x (b)._WS1_weapons y (a)._WS1_weapons) mergeop ((a)._WS2_weapons x (b)._WS2_weapons y (a)._WS2_weapons)) +# define WEPSET_OP1_EA(a,b,mergeop,x) (((a)._WS1_weapons x _WS1_##b) mergeop ((a)._WS2_weapons x _WS2_##b)) +# define WEPSET_OP2_EA(a,b,mergeop,x,y) (((a)._WS1_weapons x _WS1_##b y (a)._WS1_weapons) mergeop ((a)._WS2_weapons x _WS2_##b y (a)._WS2_weapons)) +# define WEPSET_OP1_EW(a,b,mergeop,x) (((a)._WS1_weapons x WEPSET_BIT1(b)) mergeop ((a)._WS2_weapons x WEPSET_BIT2(b))) +# define WEPSET_OP2_EW(a,b,mergeop,x,y) (((a)._WS1_weapons x WEPSET_BIT1(b) y (a)._WS1_weapons) mergeop ((a)._WS2_weapons x WEPSET_BIT2(b) y (a)._WS2_weapons)) +# define WEPSET_OP1_AE(a,b,mergeop,x) ((_WS1_##a x (b)._WS1_weapons) mergeop (_WS2_##a x (b)._WS2_weapons)) +# define WEPSET_OP2_AE(a,b,mergeop,x,y) ((_WS1_##a x (b)._WS1_weapons y _WS1_##a) mergeop (_WS2_##a x (b)._WS2_weapons y _WS2_##a)) +# define WEPSET_OP1_AA(a,b,mergeop,x) ((_WS1_##a x _WS1_##b) mergeop (_WS2_##a x _WS2_##b)) +# define WEPSET_OP2_AA(a,b,mergeop,x,y) ((_WS1_##a x _WS1_##b y _WS1_##a) mergeop (_WS2_##a x _WS2_##b y _WS2_##a)) +# define WEPSET_OP1_AW(a,b,mergeop,x) ((_WS1_##a x WEPSET_BIT1(b)) mergeop (_WS2_##a x WEPSET_BIT2(b))) +# define WEPSET_OP2_AW(a,b,mergeop,x,y) ((_WS1_##a x WEPSET_BIT1(b) y _WS1_##a) mergeop (_WS2_##a x WEPSET_BIT2(b) y _WS2_##a)) +#endif + +#define XX , + +#define WEPSET_COPY_EE(a,b) WEPSET_OP1_EE(a,b,XX,=) +#define WEPSET_EQ_EE(a,b) WEPSET_OP1_EE(a,b,&&,==) +#define WEPSET_OR_EE(a,b) WEPSET_OP1_EE(a,b,XX,|=) +#define WEPSET_AND_EE(a,b) WEPSET_OP2_EE(a,b,XX,=,&) +#define WEPSET_ANDNOT_EE(a,b) WEPSET_OP1_EE(a,b,XX,&~=) +#define WEPSET_CONTAINS_ANY_EE(a,b) !!(WEPSET_OP1_EE(a,b,||,&)) +#define WEPSET_CONTAINS_ALL_EE(a,b) WEPSET_OP2_EE(b,a,&&,==,&) + +#define WEPSET_COPY_EA(a,b) WEPSET_OP1_EA(a,b,XX,=) +#define WEPSET_EQ_EA(a,b) WEPSET_OP1_EA(a,b,&&,==) +#define WEPSET_OR_EA(a,b) WEPSET_OP1_EA(a,b,XX,|=) +#define WEPSET_AND_EA(a,b) WEPSET_OP2_EA(a,b,XX,=,&) +#define WEPSET_ANDNOT_EA(a,b) WEPSET_OP1_EA(a,b,XX,&~=) +#define WEPSET_CONTAINS_ANY_EA(a,b) !!(WEPSET_OP1_EA(a,b,||,&)) +#define WEPSET_CONTAINS_ALL_EA(a,b) WEPSET_OP2_EA(b,a,&&,==,&) + +#define WEPSET_COPY_EW(a,b) WEPSET_OP1_EW(a,b,XX,=) +#define WEPSET_EQ_EW(a,b) WEPSET_OP1_EW(a,b,&&,==) +#define WEPSET_OR_EW(a,b) WEPSET_OP1_EW(a,b,XX,|=) +#define WEPSET_AND_EW(a,b) WEPSET_OP2_EW(a,b,XX,=,&) +#define WEPSET_ANDNOT_EW(a,b) WEPSET_OP1_EW(a,b,XX,&~=) +#define WEPSET_CONTAINS_EW(a,b) !!(WEPSET_OP1_EW(a,b,||,&)) + +#define WEPSET_COPY_AE(a,b) WEPSET_OP1_AE(a,b,XX,=) +#define WEPSET_EQ_AE(a,b) WEPSET_OP1_AE(a,b,&&,==) +#define WEPSET_OR_AE(a,b) WEPSET_OP1_AE(a,b,XX,|=) +#define WEPSET_AND_AE(a,b) WEPSET_OP2_AE(a,b,XX,=,&) +#define WEPSET_ANDNOT_AE(a,b) WEPSET_OP1_AE(a,b,XX,&~=) +#define WEPSET_CONTAINS_ANY_AE(a,b) !!(WEPSET_OP1_AE(a,b,||,&)) +#define WEPSET_CONTAINS_ALL_AE(a,b) WEPSET_OP2_AE(b,a,&&,==,&) + +#define WEPSET_COPY_AA(a,b) WEPSET_OP1_AA(a,b,XX,=) +#define WEPSET_EQ_AA(a,b) WEPSET_OP1_AA(a,b,&&,==) +#define WEPSET_OR_AA(a,b) WEPSET_OP1_AA(a,b,XX,|=) +#define WEPSET_AND_AA(a,b) WEPSET_OP2_AA(a,b,XX,=,&) +#define WEPSET_ANDNOT_AA(a,b) WEPSET_OP1_AA(a,b,XX,&~=) +#define WEPSET_CONTAINS_ANY_AA(a,b) !!(WEPSET_OP1_AA(a,b,||,&)) +#define WEPSET_CONTAINS_ALL_AA(a,b) WEPSET_OP2_AA(b,a,&&,==,&) + +#define WEPSET_COPY_AW(a,b) WEPSET_OP1_AW(a,b,XX,=) +#define WEPSET_EQ_AW(a,b) WEPSET_OP1_AW(a,b,&&,==) +#define WEPSET_OR_AW(a,b) WEPSET_OP1_AW(a,b,XX,|=) +#define WEPSET_AND_AW(a,b) WEPSET_OP2_AW(a,b,XX,=,&) +#define WEPSET_ANDNOT_AW(a,b) WEPSET_OP1_AW(a,b,XX,&~=) +#define WEPSET_CONTAINS_AW(a,b) !!(WEPSET_OP1_AW(a,b,||,&)) + +WEPSET_DECLARE_A(WEPBIT_ALL); +WEPSET_DECLARE_A(WEPBIT_SUPERWEAPONS); +// note: the fabs call is just there to hide "if result is constant" warning +#define REGISTER_WEAPON_2(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ float id; \ - float bit; \ float func(float); \ void RegisterWeapons_##id() \ { \ WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \ - WEPBIT_ALL |= (bit = power2of(WEP_COUNT)); \ - WEPBIT_SUPERWEAPONS |= (bit = power2of(WEP_COUNT)) * !!(weapontype & WEP_FLAG_SUPERWEAPON); \ + WEPSET_OR_AW(WEPBIT_ALL, id); \ + if(fabs(weapontype & WEP_FLAG_SUPERWEAPON)) \ + WEPSET_OR_AW(WEPBIT_SUPERWEAPONS, id); \ ++WEP_COUNT; \ register_weapon(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \ } \ ACCUMULATE_FUNCTION(RegisterWeapons, RegisterWeapons_##id) #ifdef MENUQC #define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ - REGISTER_WEAPON_2(WEP_##id,WEPBIT_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) + REGISTER_WEAPON_2(WEP_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) #else #define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ - REGISTER_WEAPON_2(WEP_##id,WEPBIT_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) + REGISTER_WEAPON_2(WEP_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) #endif #include "../server/w_all.qc" @@ -128,3 +240,5 @@ string W_NameWeaponOrder(string order); string W_FixWeaponOrder_BuildImpulseList(string o); string W_FixWeaponOrder_AllowIncomplete(string order); string W_FixWeaponOrder_ForceComplete(string order); + +void W_RandomWeapons(entity e, float n); diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 9dd02ca4c2..05c93865db 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -242,6 +242,8 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp spawnpoints = 0; spawnplaces = 0; _MapInfo_Map_worldspawn_music = ""; + mapMins = '0 0 0'; + mapMaxs = '0 0 0'; for(;;) { @@ -536,7 +538,7 @@ string _MapInfo_GetDefaultEx(float t) { entity e; for(e = MapInfo_Type_first; e; e = e.enemy) - if(t == e.weapons) + if(t == e.items) return e.model2; return ""; } @@ -659,7 +661,7 @@ float MapInfo_Type_FromString(string t) return MAPINFO_TYPE_ALL; for(e = MapInfo_Type_first; e; e = e.enemy) if(t == e.mdl) - return e.weapons; + return e.items; return 0; } @@ -669,7 +671,7 @@ string MapInfo_Type_ToString(float t) if(t == MAPINFO_TYPE_ALL) return "all"; for(e = MapInfo_Type_first; e; e = e.enemy) - if(t == e.weapons) + if(t == e.items) return e.mdl; return ""; } @@ -678,7 +680,7 @@ string MapInfo_Type_ToText(float t) { entity e; for(e = MapInfo_Type_first; e; e = e.enemy) - if(t == e.weapons) + if(t == e.items) return e.message; return _("@!#%'n Tuba Throwing"); } @@ -943,7 +945,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, float pAllowGenerate, flo MapInfo_Map_author = s; else if(t == "has") { - t = car(s); s = cdr(s); + t = car(s); // s = cdr(s); if (t == "weapons") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS; else if(t == "turrets") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_TURRETS; else if(t == "vehicles") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_VEHICLES; @@ -1175,8 +1177,8 @@ float MapInfo_CurrentGametype() prev = cvar("gamecfg"); for(e = MapInfo_Type_first; e; e = e.enemy) if(cvar(e.netname)) - if(prev != e.weapons) - return e.weapons; + if(prev != e.items) + return e.items; if(prev) return prev; return MAPINFO_TYPE_DEATHMATCH; @@ -1205,7 +1207,7 @@ void MapInfo_SwitchGameType(float t) { entity e; for(e = MapInfo_Type_first; e; e = e.enemy) - cvar_set(e.netname, (t == e.weapons) ? "1" : "0"); + cvar_set(e.netname, (t == e.items) ? "1" : "0"); } void MapInfo_LoadMap(string s, float reinit) diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index a3bb4efbfe..bd67f67a82 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -1,9 +1,9 @@ -var float MAPINFO_TYPE_ALL = 0; +float MAPINFO_TYPE_ALL; entity MapInfo_Type_first; entity MapInfo_Type_last; .entity enemy; // internal next pointer -.float weapons; // game type ID +.float items; // game type ID .string netname; // game type name as in cvar (with g_ prefix) .string mdl; // game type short name .string message; // human readable name @@ -17,7 +17,7 @@ entity MapInfo_Type_last; MAPINFO_TYPE_##NAME = MAPINFO_TYPE_ALL + 1; \ MAPINFO_TYPE_ALL |= MAPINFO_TYPE_##NAME; \ MapInfo_Type##g_name = spawn(); \ - MapInfo_Type##g_name.weapons = MAPINFO_TYPE_##NAME; \ + MapInfo_Type##g_name.items = MAPINFO_TYPE_##NAME; \ MapInfo_Type##g_name.netname = #g_name; \ MapInfo_Type##g_name.mdl = #sname; \ MapInfo_Type##g_name.message = hname; \ diff --git a/qcsrc/common/util-pre.qh b/qcsrc/common/util-pre.qh index 518a5698d3..da86e98014 100644 --- a/qcsrc/common/util-pre.qh +++ b/qcsrc/common/util-pre.qh @@ -1,9 +1,14 @@ #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 # define COMPAT_XON050_ENGINE # define COMPAT_NO_MOD_IS_XONOTIC +# define COMPAT_XON060_DONTCRASH_CHECKPVS #endif + diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 582777647c..53c14cfb0c 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -463,6 +463,11 @@ string ScoreString(float pFlags, float pValue) return valstr; } +float dotproduct(vector a, vector b) +{ + return a_x * b_x + a_y * b_y + a_z * b_z; +} + vector cross(vector a, vector b) { return @@ -859,6 +864,8 @@ float cvar_settemp(string tmp_cvar, string tmp_value) { float created_saved_value; entity e; + + created_saved_value = FALSE; if not(tmp_cvar || tmp_value) { @@ -1578,6 +1585,109 @@ vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0 return v; } +vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style) +{ + vector ret; + + // make origin and speed relative + eorg -= myorg; + if(newton_style) + evel -= myvel; + + // now solve for ret, ret normalized: + // eorg + t * evel == t * ret * spd + // or, rather, solve for t: + // |eorg + t * evel| == t * spd + // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2 + // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0 + vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg); + // p = 2 * (eorg * evel) / (evel * evel - spd * spd) + // q = (eorg * eorg) / (evel * evel - spd * spd) + if(!solution_z) // no real solution + { + // happens if D < 0 + // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2 + // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2 + // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2 + // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg)) + // spd^2 < evel^2 * sin^2 angle(evel, eorg) + // spd < |evel| * sin angle(evel, eorg) + return '0 0 0'; + } + else if(solution_x > 0) + { + // both solutions > 0: take the smaller one + // happens if p < 0 and q > 0 + ret = normalize(eorg + solution_x * evel); + } + else if(solution_y > 0) + { + // one solution > 0: take the larger one + // happens if q < 0 or q == 0 and p < 0 + ret = normalize(eorg + solution_y * evel); + } + else + { + // no solution > 0: reject + // happens if p > 0 and q >= 0 + // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0 + // (eorg * eorg) / (evel * evel - spd * spd) >= 0 + // + // |evel| >= spd + // eorg * evel > 0 + // + // "Enemy is moving away from me at more than spd" + return '0 0 0'; + } + + // NOTE: we always got a solution if spd > |evel| + + if(newton_style == 2) + ret = normalize(ret * spd + myvel); + + return ret; +} + +vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma) +{ + if(!newton_style) + return spd * mydir; + + if(newton_style == 2) + { + // true Newtonian projectiles with automatic aim adjustment + // + // solve: |outspeed * mydir - myvel| = spd + // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0 + // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2) + // PLUS SIGN! + // not defined? + // then... + // myvel^2 - (mydir * myvel)^2 > spd^2 + // velocity without mydir component > spd + // fire at smallest possible spd that works? + // |(mydir * myvel) * myvel - myvel| = spd + + vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd); + + float outspeed; + if(solution_z) + outspeed = solution_y; // the larger one + else + { + //outspeed = 0; // slowest possible shot + outspeed = solution_x; // the real part (that is, the average!) + //dprint("impossible shot, adjusting\n"); + } + + outspeed = bound(spd * mi, outspeed, spd * ma); + return mydir * outspeed; + } + + // real Newtonian + return myvel + spd * mydir; +} + void check_unacceptable_compiler_bugs() { if(cvar("_allow_unacceptable_compiler_bugs")) @@ -1825,6 +1935,7 @@ float matchacl(string acl, string str) while(acl) { t = car(acl); acl = cdr(acl); + d = 1; if(substring(t, 0, 1) == "-") { @@ -1833,10 +1944,11 @@ float matchacl(string acl, string str) } else if(substring(t, 0, 1) == "+") t = substring(t, 1, strlen(t) - 1); + if(substring(t, -1, 1) == "*") { t = substring(t, 0, strlen(t) - 1); - s = substring(s, 0, strlen(t)); + s = substring(str, 0, strlen(t)); } else s = str; @@ -2257,3 +2369,97 @@ void queue_to_execute_next_frame(string s) } to_execute_next_frame = strzone(s); } + +float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x) +{ + return + ((( startspeedfactor + endspeedfactor - 2 + ) * x - 2 * startspeedfactor - endspeedfactor + 3 + ) * x + startspeedfactor + ) * x; +} + +float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor) +{ + if(startspeedfactor < 0 || endspeedfactor < 0) + return FALSE; + + /* + // if this is the case, the possible zeros of the first derivative are outside + // 0..1 + We can calculate this condition as condition + if(se <= 3) + return TRUE; + */ + + // better, see below: + if(startspeedfactor <= 3 && endspeedfactor <= 3) + return TRUE; + + // if this is the case, the first derivative has no zeros at all + float se = startspeedfactor + endspeedfactor; + float s_e = startspeedfactor - endspeedfactor; + if(3 * (se - 4) * (se - 4) + s_e * s_e <= 12) // an ellipse + return TRUE; + + // Now let s <= 3, s <= 3, s+e >= 3 (triangle) then we get se <= 6 (top right corner). + // we also get s_e <= 6 - se + // 3 * (se - 4)^2 + (6 - se)^2 + // is quadratic, has value 12 at 3 and 6, and value < 12 in between. + // Therefore, above "better" check works! + + return FALSE; + + // known good cases: + // (0, [0..3]) + // (0.5, [0..3.8]) + // (1, [0..4]) + // (1.5, [0..3.9]) + // (2, [0..3.7]) + // (2.5, [0..3.4]) + // (3, [0..3]) + // (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; +} diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 76b2f28d1a..faa605f887 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -1,7 +1,13 @@ // a dummy macro that prevents the "hanging ;" warning #define ENDS_WITH_CURLY_BRACE -#define ACCUMULATE_FUNCTION(func,otherfunc) \ +#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) \ #ifdef func \ void __merge__##otherfunc() { func(); otherfunc(); } \ #undef func \ @@ -9,6 +15,27 @@ #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 @@ -74,6 +101,7 @@ string mmssss(float t); string ScoreString(float vflags, float value); +float dotproduct(vector a, vector b); vector cross(vector a, vector b); void compressShortVector_init(); @@ -163,6 +191,9 @@ vector solve_quadratic(float a, float b, float c); // z = 1 if a real solution exists, 0 if not // if no real solution exists, x contains the real part and y the imaginary part of the complex solutions x+iy and x-iy +vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style); +vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma); + void check_unacceptable_compiler_bugs(); float compressShotOrigin(vector v); @@ -309,3 +340,24 @@ float ReadApproxPastTime(); // execute-stuff-next-frame subsystem void execute_next_frame(); void queue_to_execute_next_frame(string s); + +// for marking written-to values as unused where it's a good idea to do this +noref float unused_float; + + + +// a function f with: +// f(0) = 0 +// f(1) = 1 +// f'(0) = startspeedfactor +// f'(1) = endspeedfactor +float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x); + +// checks whether f'(x) = 0 anywhere from 0 to 1 +// 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); diff --git a/qcsrc/csqcmodellib/cl_model.qc b/qcsrc/csqcmodellib/cl_model.qc index 3b54263f71..163a5126d3 100644 --- a/qcsrc/csqcmodellib/cl_model.qc +++ b/qcsrc/csqcmodellib/cl_model.qc @@ -208,6 +208,7 @@ void CSQCModel_Read(float isnew) if(sf & CSQCMODEL_PROPERTY_MODELINDEX) setmodelindex(self, self.modelindex); // this retrieves the .model key and sets mins/maxs/absmin/absmax + // FIXME do we WANT this to override mins/maxs? if(sf & CSQCMODEL_PROPERTY_TELEPORTED) { diff --git a/qcsrc/csqcmodellib/sv_model.qc b/qcsrc/csqcmodellib/sv_model.qc index bf6eefa697..e6e4cfe43a 100644 --- a/qcsrc/csqcmodellib/sv_model.qc +++ b/qcsrc/csqcmodellib/sv_model.qc @@ -29,6 +29,10 @@ float CSQCModel_Send(entity to, float sf) float islocalplayer = (self == to); float isnolocalplayer = (isplayer && (self != to)); + unused_float = isplayer; + unused_float = islocalplayer; + unused_float = isnolocalplayer; + WriteByte(MSG_ENTITY, ENT_CLIENT_MODEL); WriteShort(MSG_ENTITY, sf); @@ -59,6 +63,10 @@ void CSQCModel_CheckUpdate() float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags + unused_float = isplayer; + unused_float = islocalplayer; + unused_float = isnolocalplayer; + #ifdef CSQCPLAYER_FORCE_UPDATES if(isplayer && time > self.csqcmodel_nextforcedupdate) { diff --git a/qcsrc/dpdefs/csprogsdefs.qc b/qcsrc/dpdefs/csprogsdefs.qc index 7b0644e3e8..c9b9ce9cbc 100644 --- a/qcsrc/dpdefs/csprogsdefs.qc +++ b/qcsrc/dpdefs/csprogsdefs.qc @@ -1403,6 +1403,39 @@ void() example_skel_player_delete = // END OF EXAMPLES FOR FTE_CSQC_SKELETONOBJECTS // +//DP_QC_ENTITYDATA +//idea: KrimZon +//darkplaces implementation: KrimZon +//builtin definitions: +float() numentityfields = #496; +string(float fieldnum) entityfieldname = #497; +float(float fieldnum) entityfieldtype = #498; +string(float fieldnum, entity ent) getentityfieldstring = #499; +float(float fieldnum, entity ent, string s) putentityfieldstring = #500; +//constants: +//Returned by entityfieldtype +float FIELD_STRING = 1; +float FIELD_FLOAT = 2; +float FIELD_VECTOR = 3; +float FIELD_ENTITY = 4; +float FIELD_FUNCTION = 6; +//description: +//Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame. +//WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers. +//numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z. +//entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever. +//entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers. +//getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned. +//putentityfieldstring puts the data returned by getentityfieldstring back into the entity. + +//DP_QC_ENTITYSTRING +void(string s) loadfromdata = #529; +void(string s) loadfromfile = #530; +void(string s) callfunction = #605; +void(float fh, entity e) writetofile = #606; +float(string s) isfunction = #607; +void(entity e, string s) parseentitydata = #608; + // assorted builtins const float STAT_MOVEVARS_TICRATE = 240; const float STAT_MOVEVARS_TIMESCALE = 241; diff --git a/qcsrc/dpdefs/dpextensions.qc b/qcsrc/dpdefs/dpextensions.qc index 0acf262b11..0a55043927 100644 --- a/qcsrc/dpdefs/dpextensions.qc +++ b/qcsrc/dpdefs/dpextensions.qc @@ -593,9 +593,11 @@ float(float a) tan = #475; // returns tangent value (which is simply sin(a)/cos( // string autocvar__cl_name; //NOTE: copying a string-typed autocvar to another variable/field, and then //changing the cvar or returning from progs is UNDEFINED. Writing to autocvar -//globals is UNDEFINED. Accessing autocvar globals after cvar_set()ing that -//cvar in the same frame is IMPLEMENTATION DEFINED (an implementation may -//either yield the previous, or the current, value). Whether autocvar globals, +//globals is UNDEFINED. Accessing autocvar globals after changing that cvar in +//the same frame by any means other than cvar_set() from the same QC VM is +//IMPLEMENTATION DEFINED (an implementation may either yield the previous, or +//the current, value). Changing them via cvar_set() in the same QC VM +//immediately must reflect on the autocvar globals. Whether autocvar globals, //after restoring a savegame, have the cvar's current value, or the original //value at time of saving, is UNDEFINED. Restoring a savegame however must not //restore the cvar values themselves. @@ -2485,6 +2487,28 @@ void(float bufhandle, float string_index) bufstr_free = #469; //cvars that start with pattern but not with antipattern will be stored into the buffer void(float bufhandle, string pattern, string antipattern) buf_cvarlist = #517; +//DP_QC_STRINGBUFFERS_EXT_WIP +//idea: VorteX +//darkplaces implementation: VorteX +//constant definitions: +const float MATCH_AUTO = 0; +const float MATCH_WHOLE = 1; +const float MATCH_LEFT = 2; +const float MATCH_RIGHT = 3; +const float MATCH_MIDDLE = 4; +const float MATCH_PATTERN = 5; +//builtin definitions: +float(string filename, float bufhandle) buf_loadfile = #535; // append each line of file as new buffer string, return 1 if succesful +float(float filehandle, float bufhandle, float startpos, float numstrings) buf_writefile = #536; // writes buffer strings as lines, returns 1 if succesful +float(float bufhandle, string match, float matchrule, float startpos, float step) bufstr_find = #537; // returns string index +float(string s, string pattern, float matchrule) matchpattern = #538; // returns 0/1 +float(string s, string pattern, float matchrule, float pos) matchpatternofs = #538; +//description: +//provides a set of functions to manipulate with string buffers +//pattern wildcards: * - any character (or no characters), ? - any 1 character +//Warning: This extension is work-in-progress, it may be changed/revamped/removed at any time, dont use it if you dont want any trouble +//wip note: UTF8 is not supported yet + //DP_QC_STRREPLACE //idea: Sajt //darkplaces implementation: Sajt diff --git a/qcsrc/dpdefs/menudefs.qc b/qcsrc/dpdefs/menudefs.qc index ad8666ed35..c119874617 100644 --- a/qcsrc/dpdefs/menudefs.qc +++ b/qcsrc/dpdefs/menudefs.qc @@ -504,6 +504,31 @@ float(string url, float id) uri_get = #513; float(string url, float id, string content_type, string data) uri_post = #513; float(string url, float id, string content_type, string delim, float buf) uri_postbuf = #513; +//DP_QC_ENTITYDATA +//idea: KrimZon +//darkplaces implementation: KrimZon +//builtin definitions: +float() numentityfields = #496; +string(float fieldnum) entityfieldname = #497; +float(float fieldnum) entityfieldtype = #498; +string(float fieldnum, entity ent) getentityfieldstring = #499; +float(float fieldnum, entity ent, string s) putentityfieldstring = #500; +//constants: +//Returned by entityfieldtype +float FIELD_STRING = 1; +float FIELD_FLOAT = 2; +float FIELD_VECTOR = 3; +float FIELD_ENTITY = 4; +float FIELD_FUNCTION = 6; +//description: +//Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame. +//WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers. +//numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z. +//entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever. +//entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers. +//getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned. +//putentityfieldstring puts the data returned by getentityfieldstring back into the entity. + // assorted undocumented extensions string(string, float) netaddress_resolve = #625; string(string search, string replace, string subject) strreplace = #484; diff --git a/qcsrc/fteqcc-bugs.qc b/qcsrc/fteqcc-bugs.qc new file mode 100644 index 0000000000..dd4fd45f3c --- /dev/null +++ b/qcsrc/fteqcc-bugs.qc @@ -0,0 +1,30 @@ +void error(...) = #1; +float id(float x) { return x; } + +void Oassignments(float foo) // pass 1 +{ + float bar; + bar = 2; + bar = (foo ? 0 : (foo & 1)); + if(bar == 2) + error("FTEQCC SUCKS"); +} + +void Oreturn_only(float foo) // pass 0 +{ + if(foo) + { + return; + } +} + +void Oreturn_only_trap(void) +{ + error("FTEQCC SUCKS"); +} + +.float fld; +void Ono_assignments(entity e, float wep) // pass an e with e.fld == 1, and wep == 3. e.fld will be 2 instead of 3. Observe the INDIRECT and the SUB use the same field for storage, wreaking havoc. +{ + ((e).fld |= id(wep - 1)); +} diff --git a/qcsrc/menu/anim/animhost.c b/qcsrc/menu/anim/animhost.c index 48a45bda84..ae84e0957d 100644 --- a/qcsrc/menu/anim/animhost.c +++ b/qcsrc/menu/anim/animhost.c @@ -29,8 +29,7 @@ void AnimHost_addAnim(entity me, entity other) other.parent = me; - entity f, l; - f = me.firstChild; + entity l; l = me.lastChild; if(l) @@ -50,9 +49,7 @@ void AnimHost_removeAnim(entity me, entity other) other.parent = NULL; - entity n, p, f, l; - f = me.firstChild; - l = me.lastChild; + entity n, p; n = other.nextSibling; p = other.prevSibling; diff --git a/qcsrc/menu/anim/keyframe.c b/qcsrc/menu/anim/keyframe.c index de5d54ae10..3bcda94e69 100644 --- a/qcsrc/menu/anim/keyframe.c +++ b/qcsrc/menu/anim/keyframe.c @@ -75,8 +75,7 @@ void Keyframe_addAnim(entity me, entity other) other.parent = me; - entity f, l; - f = me.firstChild; + entity l; l = me.lastChild; if(l) diff --git a/qcsrc/menu/draw.qc b/qcsrc/menu/draw.qc index 1f223f281a..9c0d032432 100644 --- a/qcsrc/menu/draw.qc +++ b/qcsrc/menu/draw.qc @@ -16,11 +16,21 @@ void draw_drawMousePointer(vector where) void draw_reset(float cw, float ch, float ox, float oy) { - drawfont = FONT_USER+0; draw_shift = '1 0 0' * ox + '0 1 0' * oy; draw_scale = '1 0 0' * cw + '0 1 0' * ch; draw_alpha = 1; draw_fontscale = '1 1 0'; + draw_endBoldFont(); +} + +void draw_beginBoldFont() +{ + drawfont = FONT_USER+3; +} + +void draw_endBoldFont() +{ + drawfont = FONT_USER+0; } vector globalToBox(vector v, vector theOrigin, vector theScale) diff --git a/qcsrc/menu/draw.qh b/qcsrc/menu/draw.qh index 301a5f440f..7178628d8b 100644 --- a/qcsrc/menu/draw.qh +++ b/qcsrc/menu/draw.qh @@ -11,6 +11,8 @@ float draw_alpha; vector draw_fontscale; void draw_reset(float cw, float ch, float ox, float oy); +void draw_beginBoldFont(); +void draw_endBoldFont(); void draw_setMousePointer(string pic, vector theSize, vector theOffset); void draw_drawMousePointer(vector where); diff --git a/qcsrc/menu/item/borderimage.c b/qcsrc/menu/item/borderimage.c index b5dc52d051..3a345a43df 100644 --- a/qcsrc/menu/item/borderimage.c +++ b/qcsrc/menu/item/borderimage.c @@ -2,6 +2,8 @@ CLASS(BorderImage) EXTENDS(Label) METHOD(BorderImage, configureBorderImage, void(entity, string, float, vector, string, float)) METHOD(BorderImage, resizeNotify, void(entity, vector, vector, vector, vector)) + METHOD(BorderImage, recalcPositionWithText, void(entity, string)) + ATTRIB(BorderImage, isBold, float, 1) METHOD(BorderImage, draw, void(entity)) ATTRIB(BorderImage, src, string, string_null) ATTRIB(BorderImage, borderHeight, float, 0) @@ -13,27 +15,38 @@ CLASS(BorderImage) EXTENDS(Label) ATTRIB(BorderImage, isNexposeeTitleBar, float, 0) ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0) ATTRIB(BorderImage, zoomedOutTitleBar, float, 0) + ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0') + ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0') + ATTRIB(BorderImage, saveRelSize, vector, '0 0 0') ENDCLASS(BorderImage) #endif #ifdef IMPLEMENTATION -void BorderImage_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) +void BorderImage_recalcPositionWithText(entity me, string t) { - me.isNexposeeTitleBar = 0; - if(me.zoomedOutTitleBar) - if(me.parent.parent.instanceOfNexposee) - if(me.parent.instanceOfDialog) - if(me == me.parent.frame) - me.isNexposeeTitleBar = 1; if(me.isNexposeeTitleBar) { vector scrs; scrs = eX * conwidth + eY * conheight; - SUPER(BorderImage).resizeNotify(me, relOrigin, relSize, boxToGlobal(me.parent.Nexposee_smallOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_smallSize, scrs)); + me.resizeNotify(me, me.saveRelOrigin, me.saveRelSize, boxToGlobal(me.parent.Nexposee_smallOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_smallSize, scrs)); + SUPER(BorderImage).recalcPositionWithText(me, t); me.realOrigin_y = me.realFontSize_y * me.zoomedOutTitleBarPosition; me.realOrigin_Nexposeed = me.realOrigin; me.realFontSize_Nexposeed = me.realFontSize; + me.resizeNotify(me, me.saveRelOrigin, me.saveRelSize, boxToGlobal(me.parent.Nexposee_initialOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_initialSize, scrs)); } + SUPER(BorderImage).recalcPositionWithText(me, t); +} +void BorderImage_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) +{ + me.isNexposeeTitleBar = 0; + if(me.zoomedOutTitleBar) + if(me.parent.parent.instanceOfNexposee) + if(me.parent.instanceOfDialog) + if(me == me.parent.frame) + me.isNexposeeTitleBar = 1; + me.saveRelOrigin = relOrigin; + me.saveRelSize = relSize; SUPER(BorderImage).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); me.borderVec_x = me.borderHeight / absSize_x; me.borderVec_y = me.borderHeight / absSize_y; @@ -59,11 +72,16 @@ void BorderImage_draw(entity me) { if(me.src) draw_BorderPicture('0 0 0', me.src, '1 1 0', me.color, 1, me.borderVec); + if(me.fontSize > 0) { - vector ro, rf, df; + if(me.recalcPos) + me.recalcPositionWithText(me, me.text); + if(me.isNexposeeTitleBar) { + vector ro, rf, df; + // me.parent.Nexposee_animationFactor 0 (small) or 1 (full) // default values are for 1 ro = me.realOrigin; @@ -72,18 +90,17 @@ void BorderImage_draw(entity me) me.realOrigin = ro * me.parent.Nexposee_animationFactor + me.realOrigin_Nexposeed * (1 - me.parent.Nexposee_animationFactor); me.realFontSize = rf * me.parent.Nexposee_animationFactor + me.realFontSize_Nexposeed * (1 - me.parent.Nexposee_animationFactor); draw_fontscale = globalToBoxSize(boxToGlobalSize(df, me.realFontSize), rf); - } - SUPER(BorderImage).draw(me); + SUPER(BorderImage).draw(me); - if(me.isNexposeeTitleBar) - { // me.Nexposee_animationState 0 (small) or 1 (full) // default values are for 1 me.realOrigin = ro; me.realFontSize = rf; draw_fontscale = df; } + else + SUPER(BorderImage).draw(me); } else { diff --git a/qcsrc/menu/item/container.c b/qcsrc/menu/item/container.c index 0e8fa9ea0a..6241cee039 100644 --- a/qcsrc/menu/item/container.c +++ b/qcsrc/menu/item/container.c @@ -304,8 +304,7 @@ void Container_addItem(entity me, entity other, vector theOrigin, vector theSize other.Container_size = theSize; me.setAlphaOf(me, other, theAlpha); - entity f, l; - f = me.firstChild; + entity l; l = me.lastChild; if(l) @@ -330,9 +329,7 @@ void Container_removeItem(entity me, entity other) other.parent = NULL; - entity n, p, f, l; - f = me.firstChild; - l = me.lastChild; + entity n, p; n = other.nextSibling; p = other.prevSibling; @@ -371,13 +368,11 @@ void Container_setFocus(entity me, entity other) void Container_moveItemAfter(entity me, entity other, entity dest) { // first: remove other from the chain - entity n, p, f, l; + entity n, p; if(other.parent != me) error("Can't move in wrong container!"); - f = me.firstChild; - l = me.lastChild; n = other.nextSibling; p = other.prevSibling; diff --git a/qcsrc/menu/item/dialog.c b/qcsrc/menu/item/dialog.c index da6a4013ce..ed73be0950 100644 --- a/qcsrc/menu/item/dialog.c +++ b/qcsrc/menu/item/dialog.c @@ -124,7 +124,6 @@ void Dialog_TDempty(entity me, float colspan) void Dialog_configureDialog(entity me) { - entity closebutton; float absWidth, absHeight; me.frame = spawnBorderImage(); @@ -154,14 +153,13 @@ void Dialog_configureDialog(entity me) if(me.closable && me.borderLines > 0) { - closebutton = me.closeButton = spawnButton(); + entity closebutton; + closebutton = me.closeButton = me.frame.closeButton = spawnButton(); closebutton.configureButton(closebutton, "", 0, me.closeButtonImage); closebutton.onClick = Dialog_Close; closebutton.onClickEntity = me; closebutton.srcMulti = 0; me.addItem(me, closebutton, '0 0 0', '1 1 0', 1); // put it as LAST } - - me.frame.closeButton = closebutton; } void Dialog_close(entity me) diff --git a/qcsrc/menu/item/inputbox.c b/qcsrc/menu/item/inputbox.c index 13351cfd32..572d3a0710 100644 --- a/qcsrc/menu/item/inputbox.c +++ b/qcsrc/menu/item/inputbox.c @@ -146,6 +146,9 @@ void InputBox_draw(entity me) if(me.pressed) me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event + if(me.recalcPos) + me.recalcPositionWithText(me, me.text); + me.focusable = !me.disabled; if(me.disabled) draw_alpha *= me.disabledAlpha; @@ -223,7 +226,6 @@ void InputBox_draw(entity me) else if(ch2 == "x") // ^x found { theColor = '1 1 1'; - theTempColor = '0 0 0'; component = HEXDIGIT_TO_DEC(substring(me.text, i+2, 1)); if (component >= 0) // ^xr found diff --git a/qcsrc/menu/item/inputcontainer.c b/qcsrc/menu/item/inputcontainer.c index e1b14d3b16..65129b294d 100644 --- a/qcsrc/menu/item/inputcontainer.c +++ b/qcsrc/menu/item/inputcontainer.c @@ -153,8 +153,7 @@ float InputContainer_mousePress(entity me, vector pos) } float InputContainer_mouseRelease(entity me, vector pos) { - float r; - r = SUPER(InputContainer).mouseRelease(me, pos); + SUPER(InputContainer).mouseRelease(me, pos); // return value? if(me.focused) // am I still eligible for this? (UGLY HACK, but a mouse event could have changed focus away) if(me._changeFocusXY(me, pos)) return 1; diff --git a/qcsrc/menu/item/label.c b/qcsrc/menu/item/label.c index 85df8a7179..378e74adbc 100644 --- a/qcsrc/menu/item/label.c +++ b/qcsrc/menu/item/label.c @@ -5,7 +5,8 @@ CLASS(Label) EXTENDS(Item) METHOD(Label, resizeNotify, void(entity, vector, vector, vector, vector)) METHOD(Label, setText, void(entity, string)) METHOD(Label, toString, string(entity)) - METHOD(Label, recalcPosition, void(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) @@ -25,6 +26,8 @@ CLASS(Label) EXTENDS(Item) ATTRIB(Label, allowWrap, float, 0) ATTRIB(Label, recalcPos, float, 0) ATTRIB(Label, condenseFactor, float, 1) + ATTRIB(Label, overrideRealOrigin, vector, '0 0 0') + ATTRIB(Label, overrideCondenseFactor, float, 0) ENDCLASS(Label) #endif @@ -38,35 +41,85 @@ void Label_setText(entity me, string txt) me.text = txt; me.recalcPos = 1; } -void Label_recalcPosition(entity me) +void Label_recalcPositionWithText(entity me, string t) { float spaceAvail; spaceAvail = 1 - me.keepspaceLeft - me.keepspaceRight; + if(me.isBold) + draw_beginBoldFont(); + float spaceUsed; - spaceUsed = draw_TextWidth(me.text, me.allowColors, me.realFontSize); + spaceUsed = draw_TextWidth(t, me.allowColors, me.realFontSize); if(spaceUsed <= spaceAvail) { - me.realOrigin_x = me.align * (spaceAvail - spaceUsed) + me.keepspaceLeft; - me.condenseFactor = 1; + if(!me.overrideRealOrigin_x) + me.realOrigin_x = me.align * (spaceAvail - spaceUsed) + me.keepspaceLeft; + if(!me.overrideCondenseFactor) + me.condenseFactor = 1; } else if(me.allowCut || me.allowWrap) { - me.realOrigin_x = me.keepspaceLeft; - me.condenseFactor = 1; + if(!me.overrideRealOrigin_x) + me.realOrigin_x = me.keepspaceLeft; + if(!me.overrideCondenseFactor) + me.condenseFactor = 1; } else { - me.realOrigin_x = me.keepspaceLeft; - me.condenseFactor = spaceAvail / spaceUsed; - dprint(sprintf(_("NOTE: label text %s too wide for label, condensed by factor %f\n"), me.text, me.condenseFactor)); + if(!me.overrideRealOrigin_x) + me.realOrigin_x = me.keepspaceLeft; + if(!me.overrideCondenseFactor) + me.condenseFactor = spaceAvail / spaceUsed; + dprint(sprintf(_("NOTE: label text %s too wide for label, condensed by factor %f\n"), t, me.condenseFactor)); + } + + if(!me.overrideRealOrigin_y) + { + float lines; + vector dfs; + vector fs; + + // set up variables to draw in condensed size, but use hinting for original size + fs = me.realFontSize; + fs_x *= me.condenseFactor; + + dfs = draw_fontscale; + draw_fontscale_x *= me.condenseFactor; + + if(me.allowCut) // FIXME allowCut incompatible with align != 0 + lines = 1; + else if(me.allowWrap) // FIXME allowWrap incompatible with align != 0 + { + getWrappedLine_remaining = me.text; + lines = 0; + while(getWrappedLine_remaining) + { + if (me.allowColors) + getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithColors); + else + getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithoutColors); + ++lines; + } + } + else + lines = 1; + + draw_fontscale = dfs; + + 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) { SUPER(Label).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + // absSize_y is height of label me.realFontSize_y = me.fontSize / absSize_y; me.realFontSize_x = me.fontSize / absSize_x; @@ -75,41 +128,7 @@ void Label_resizeNotify(entity me, vector relOrigin, vector relSize, vector absO if(me.marginRight) me.keepspaceRight = me.marginRight * me.realFontSize_x; - me.recalcPosition(me); - - float lines; - - vector dfs; - vector fs; - - // set up variables to draw in condensed size, but use hinting for original size - fs = me.realFontSize; - fs_x *= me.condenseFactor; - - dfs = draw_fontscale; - draw_fontscale_x *= me.condenseFactor; - - if(me.allowCut) // FIXME allowCut incompatible with align != 0 - lines = 1; - else if(me.allowWrap) // FIXME allowWrap incompatible with align != 0 - { - getWrappedLine_remaining = me.text; - lines = 0; - while(getWrappedLine_remaining) - { - if (me.allowColors) - getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithColors); - else - getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithoutColors); - ++lines; - } - } - else - lines = 1; - - draw_fontscale = dfs; - - me.realOrigin_y = 0.5 * (1 - lines * me.realFontSize_y); + me.recalcPos = 1; } void Label_configureLabel(entity me, string txt, float sz, float algn) { @@ -133,7 +152,7 @@ void Label_draw(entity me) t = me.text; if(me.recalcPos) - me.recalcPosition(me); + me.recalcPositionWithText(me, t); if(me.fontSize) if(t) @@ -141,6 +160,9 @@ void Label_draw(entity me) vector dfs; vector fs; + if(me.isBold) + draw_beginBoldFont(); + // set up variables to draw in condensed size, but use hinting for original size fs = me.realFontSize; fs_x *= me.condenseFactor; @@ -168,6 +190,9 @@ void Label_draw(entity me) draw_Text(me.realOrigin, t, fs, me.colorL, me.alpha, me.allowColors); draw_fontscale = dfs; + + if(me.isBold) + draw_endBoldFont(); } SUPER(Label).draw(me); diff --git a/qcsrc/menu/item/listbox.c b/qcsrc/menu/item/listbox.c index 4f8dca622c..60da88a09c 100644 --- a/qcsrc/menu/item/listbox.c +++ b/qcsrc/menu/item/listbox.c @@ -167,7 +167,6 @@ float ListBox_mousePress(entity me, vector pos) } float ListBox_mouseRelease(entity me, vector pos) { - vector absSize; if(me.pressed == 1) { // slider dragging mode @@ -182,7 +181,6 @@ float ListBox_mouseRelease(entity me, vector pos) // and give it a nice click event if(me.nItems > 0) { - absSize = boxToGlobalSize(me.size, eX * (1 - me.controlWidth) + eY * me.itemHeight); me.clickListBoxItem(me, me.selectedItem, globalToBox(pos, eY * (me.selectedItem * me.itemHeight - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.itemHeight)); } } diff --git a/qcsrc/menu/item/modalcontroller.c b/qcsrc/menu/item/modalcontroller.c index 4f1ff4e9ed..5a5541dc7b 100644 --- a/qcsrc/menu/item/modalcontroller.c +++ b/qcsrc/menu/item/modalcontroller.c @@ -144,6 +144,7 @@ void ModalController_draw(entity me) vector fs; animating = 0; + front = world; for(e = me.firstChild; e; e = e.nextSibling) if(e.ModalController_state) { diff --git a/qcsrc/menu/item/nexposee.c b/qcsrc/menu/item/nexposee.c index 02f1224252..8f8f45119f 100644 --- a/qcsrc/menu/item/nexposee.c +++ b/qcsrc/menu/item/nexposee.c @@ -305,6 +305,7 @@ float Nexposee_keyDown(entity me, float scan, float ascii, float shift) } switch(me.animationState) { + default: case 0: case 3: nexposeeKey = ((scan == K_SPACE) || (scan == K_ENTER) || (scan == K_KP_ENTER)); @@ -318,6 +319,7 @@ float Nexposee_keyDown(entity me, float scan, float ascii, float shift) { switch(me.animationState) { + default: case 0: case 3: me.animationState = 1; diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index b541a7f063..d3bf2ec629 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -73,8 +73,8 @@ void m_init() } // needs to be done so early because of the constants they create - RegisterWeapons(); - RegisterGametypes(); + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); float ddsload = cvar("r_texture_dds_load"); float texcomp = cvar("gl_texturecompression"); @@ -344,6 +344,7 @@ void drawBackground(string img, float a, string algn, float force1) scalemode = SCALEMODE_CROP; + l = 0; for(i = 0; i < strlen(algn); ++i) { c = substring(algn, i, 1); diff --git a/qcsrc/menu/xonotic/colorbutton.c b/qcsrc/menu/xonotic/colorbutton.c index 4e81a4acc2..e0f4c5b2e4 100644 --- a/qcsrc/menu/xonotic/colorbutton.c +++ b/qcsrc/menu/xonotic/colorbutton.c @@ -70,6 +70,9 @@ void XonoticColorButton_loadCvars(entity me) if not(me.cvarName) return; + if(cvar_string(me.cvarName) == cvar_defstring(me.cvarName)) + cvar_set(me.cvarName, ftos(16 * floor(random() * 15) + floor(random() * 15))); + if(me.cvarPart == 1) me.checked = (cvar(me.cvarName) & 240) == me.cvarValueFloat * 16; else diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c b/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c index 226f346b64..3fcabaaab6 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_advanced.c @@ -25,36 +25,36 @@ void XonoticAdvancedDialog_fill(entity me) me.TD(me, 1, 3, makeXonoticTextLabel(0, _("Game settings:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2.8, e = makeXonoticCheckBox(0, "sv_spectate", _("Allow spectating"))); + me.TD(me, 1, 2.8, makeXonoticCheckBox(0, "sv_spectate", _("Allow spectating"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, _("Spawn shield:"))); - me.TD(me, 1, 1.6, e = makeXonoticSlider(0, 15, 0.5, "g_spawnshieldtime")); + me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Spawn shield:"))); + me.TD(me, 1, 1.6, makeXonoticSlider(0, 15, 0.5, "g_spawnshieldtime")); me.TR(me); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, _("Game speed:"))); - me.TD(me, 1, 1.6, e = makeXonoticSlider(0.5, 2.0, 0.1, "slowmo")); + me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Game speed:"))); + me.TD(me, 1, 1.6, makeXonoticSlider(0.5, 2.0, 0.1, "slowmo")); me.TR(me); me.TR(me); me.TD(me, 1, 3, makeXonoticTextLabel(0, _("Teamplay settings:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, _("Friendly fire scale:"))); - me.TD(me, 1, 1.6, e = makeXonoticSlider(0, 1.0, 0.05, "g_friendlyfire")); + me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Friendly fire scale:"))); + me.TD(me, 1, 1.6, makeXonoticSlider(0, 1.0, 0.05, "g_friendlyfire")); me.TR(me); me.TDempty(me, 0.4); - me.TD(me, 1, 2.6, e = makeXonoticCheckBox(0, "g_friendlyfire_virtual", _("Virtual friendly fire (effect only)"))); + me.TD(me, 1, 2.6, makeXonoticCheckBox(0, "g_friendlyfire_virtual", _("Virtual friendly fire (effect only)"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, _("Friendly fire penalty:"))); - me.TD(me, 1, 1.6, e = makeXonoticSlider(0, 1.0, 0.05, "g_mirrordamage")); + me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Friendly fire penalty:"))); + me.TD(me, 1, 1.6, makeXonoticSlider(0, 1.0, 0.05, "g_mirrordamage")); me.TR(me); me.TDempty(me, 0.4); - me.TD(me, 1, 2.6, e = makeXonoticCheckBox(0, "g_mirrordamage_virtual", _("Virtual penalty (effect only)"))); + me.TD(me, 1, 2.6, makeXonoticCheckBox(0, "g_mirrordamage_virtual", _("Virtual penalty (effect only)"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, _("Teams:"))); + me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Teams:"))); me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_keyhunt_teams_override")); e.addValue(e, "Default", "0"); e.addValue(e, "2 teams", "2"); @@ -63,7 +63,7 @@ void XonoticAdvancedDialog_fill(entity me) e.configureXonoticTextSliderValues(e); me.TR(me); me.TR(me); - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Map voting:"))); + me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Map voting:"))); me.TD(me, 1, 2, e = makeXonoticTextSlider("g_maplist_votable")); e.addValue(e, _("No voting"), "0"); e.addValue(e, _("2 choices"), "2"); @@ -76,7 +76,7 @@ void XonoticAdvancedDialog_fill(entity me) e.addValue(e, _("9 choices"), "9"); e.configureXonoticTextSliderValues(e); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBoxEx(0.5, 0, "sv_vote_simple_majority_factor", _("Simple majority wins vcall"))); + me.TD(me, 1, 3, makeXonoticCheckBoxEx(0.5, 0, "sv_vote_simple_majority_factor", _("Simple majority wins vcall"))); me.gotoRC(me, me.rows - 1, 0); me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0')); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c index 328e3d79e0..e4128235f2 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c @@ -7,7 +7,7 @@ CLASS(XonoticMutatorsDialog) EXTENDS(XonoticDialog) ATTRIB(XonoticMutatorsDialog, title, string, _("Mutators")) ATTRIB(XonoticMutatorsDialog, color, vector, SKINCOLOR_DIALOG_MUTATORS) ATTRIB(XonoticMutatorsDialog, intendedWidth, float, 0.9) - ATTRIB(XonoticMutatorsDialog, rows, float, 17) + ATTRIB(XonoticMutatorsDialog, rows, float, 19) ATTRIB(XonoticMutatorsDialog, columns, float, 6) ATTRIB(XonoticMutatorsDialog, refilterEntity, entity, NULL) ENDCLASS(XonoticMutatorsDialog) @@ -54,7 +54,7 @@ string WeaponArenaString() } } s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3)); - + weaponarenastring = strzone(s); return weaponarenastring; @@ -68,6 +68,8 @@ string XonoticMutatorsDialog_toString(entity me) s = strcat(s, ", ", _("Dodging")); if(cvar("g_minstagib")) s = strcat(s, ", ", _("MinstaGib")); + if(cvar("g_new_toys")) + s = strcat(s, ", ", _("New Toys")); if(cvar("g_nix")) s = strcat(s, ", ", _("NIX")); if(cvar("g_rocket_flying")) @@ -161,7 +163,6 @@ void preDrawLaserWeaponArenaLaserButton(entity me) float checkCompatibility_pinata(entity me) { - string s; if(cvar("g_minstagib")) return 0; if(cvar("g_nix")) @@ -174,6 +175,18 @@ float checkCompatibility_weaponstay(entity me) { return checkCompatibility_pinata(me); } +float checkCompatibility_newtoys(entity me) +{ + if(cvar("g_minstagib")) + return 0; + if(cvar_string("g_weaponarena") == "most") + return 1; + if(cvar_string("g_weaponarena") == "all") + return 1; + if(cvar_string("g_weaponarena") != "0") + return 0; + return 1; +} void XonoticMutatorsDialog_fill(entity me) { @@ -184,55 +197,59 @@ void XonoticMutatorsDialog_fill(entity me) me.TD(me, 1, 2, makeXonoticTextLabel(0, _("Gameplay mutators:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_dodging", _("Dodging"))); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_dodging", _("Dodging"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_cloaked", _("Cloaked"))); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_cloaked", _("Cloaked"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_midair", _("Midair"))); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_midair", _("Midair"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_vampire", _("Vampire"))); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_vampire", _("Vampire"))); me.TR(me); me.TDempty(me, 0.2); s = makeXonoticSlider(10, 50, 1, "g_bloodloss"); - me.TD(me, 1, 2, e = makeXonoticSliderCheckBox(0, 1, s, _("Blood loss"))); + me.TD(me, 1, 1.8, e = makeXonoticSliderCheckBox(0, 1, s, _("Blood loss"))); setDependent(e, "g_minstagib", 0, 0); me.TR(me); me.TDempty(me, 0.4); - me.TD(me, 1, 1.8, s); + me.TD(me, 1, 1.6, s); me.TR(me); me.TDempty(me, 0.2); s = makeXonoticSlider(80, 400, 8, "sv_gravity"); s.valueDigits = 0; s.valueDisplayMultiplier = 0.125; // show gravity in percent - me.TD(me, 1, 2, e = makeXonoticSliderCheckBox(800, 1, s, _("Low gravity"))); + me.TD(me, 1, 1.8, e = makeXonoticSliderCheckBox(800, 1, s, _("Low gravity"))); e.savedValue = 200; // good on silvercity me.TR(me); me.TDempty(me, 0.4); - me.TD(me, 1, 1.8, s); + me.TD(me, 1, 1.6, s); me.TR(me); me.TD(me, 1, 2, makeXonoticTextLabel(0, _("Weapon & item mutators:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_grappling_hook", _("Grappling hook"))); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_grappling_hook", _("Grappling hook"))); + me.TR(me); + me.TDempty(me, 0.2); + me.TD(me, 1, 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_jetpack", _("Jet pack"))); + 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_invincible_projectiles", _("Invincible Projectiles"))); + 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); @@ -240,7 +257,7 @@ void XonoticMutatorsDialog_fill(entity me) me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Weapon arenas:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)"))); for(i = WEP_FIRST, j = 0; i <= WEP_LAST; ++i) { w = get_weaponinfo(i); @@ -251,7 +268,7 @@ void XonoticMutatorsDialog_fill(entity me) str = w.netname; hstr = w.message; me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_weaponarena", strzone(str), strzone(hstr))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", strzone(str), strzone(hstr))); e.cvarOffValue = "0"; // custom load/save logic that ignores a " laser" suffix, or adds it e.loadCvars = loadCvarsLaserWeaponArenaWeaponButton; @@ -261,7 +278,7 @@ void XonoticMutatorsDialog_fill(entity me) } me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1, e = makeXonoticCheckBox(0, "menu_weaponarena_with_laser", _("with laser"))); + me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "menu_weaponarena_with_laser", _("with laser"))); // hook the draw function to gray it out e.draw_weaponarena = e.draw; e.draw = preDrawLaserWeaponArenaLaserButton; @@ -272,21 +289,21 @@ void XonoticMutatorsDialog_fill(entity me) me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Special arenas:"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_minstagib", string_null, _("MinstaGib"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_minstagib", string_null, _("MinstaGib"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_nix", string_null, _("NIX"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_nix", string_null, _("NIX"))); me.TR(me); me.TDempty(me, 0.4); - me.TD(me, 1, 1, e = makeXonoticCheckBox(0, "g_nix_with_laser", _("with laser"))); + me.TD(me, 1, 1.6, e = makeXonoticCheckBox(0, "g_nix_with_laser", _("with laser"))); setDependent(e, "g_nix", 1, 1); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_weaponarena", "most", _("Most weapons"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", "most", _("Most weapons"))); e.cvarOffValue = "0"; me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons"))); e.cvarOffValue = "-1"; makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_rifle g_start_weapon_hlac g_start_weapon_seeker g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_porto g_start_weapon_tuba g_start_weapon_minelayer"); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c b/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c index d33a1eab12..ef930b4e14 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c @@ -31,7 +31,7 @@ void XonoticPlayerSettingsTab_draw(entity me) void XonoticPlayerSettingsTab_fill(entity me) { entity e, pms, label, box; - float i, r, m, n; + float i; me.TR(me); me.TD(me, 1, 0.5, me.playerNameLabel = makeXonoticTextLabel(0, _("Name:"))); @@ -54,34 +54,38 @@ void XonoticPlayerSettingsTab_fill(entity me) me.TR(me); me.TR(me); me.TR(me); + + me.TR(me); + me.TDempty(me, 1); + me.TD(me, 1, 2, e = makeXonoticTextLabel(0.5, _("Model:"))); me.TR(me); - me.gotoRC(me, 8, 0.0); + me.TDempty(me, 1); pms = makeXonoticPlayerModelSelector(); - me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Model:"))); me.TD(me, 1, 0.3, e = makeXonoticButton("<<", '0 0 0')); e.onClick = PlayerModelSelector_Prev_Click; e.onClickEntity = pms; - me.TD(me, me.rows - (me.currentRow + 2), 1.8, pms); + me.TD(me, me.rows - (me.currentRow + 2), 1.4, pms); me.TD(me, 1, 0.3, e = makeXonoticButton(">>", '0 0 0')); e.onClick = PlayerModelSelector_Next_Click; e.onClickEntity = pms; me.TR(me); - r = me.currentRow; - m = me.rows - (r + 3); - n = 16 - !cvar("developer"); - m = m / (n - 1); - for(i = 0; i < n; ++i) + me.TD(me, 1, 1, e = makeXonoticTextLabel(0.5, _("Glowing color:"))); + for(i = 0; i < 15; ++i) { - me.gotoRC(me, r + i * m, 0.1); - me.TDNoMargin(me, m, 0.2, e = makeXonoticColorButton(1, 0, i), '0 1 0'); + if(mod(i, 5) == 0) + me.TR(me); + me.TDNoMargin(me, 1, 0.2, e = makeXonoticColorButton(1, 0, i), '0 1 0'); } - for(i = 0; i < n; ++i) + me.TR(me); + me.TR(me); + me.TD(me, 1, 1, e = makeXonoticTextLabel(0.5, _("Detail color:"))); + for(i = 0; i < 15; ++i) { - me.gotoRC(me, r + i * m, 0.4); - me.TDNoMargin(me, m, 0.2, e = makeXonoticColorButton(2, 1, i), '0 1 0'); + if(mod(i, 5) == 0) + me.TR(me); + me.TDNoMargin(me, 1, 0.2, e = makeXonoticColorButton(2, 1, i), '0 1 0'); } - // crosshair_enabled: 0 = no crosshair options, 1 = no crosshair selection, but everything else enabled, 2 = all crosshair options enabled // FIXME: In the future, perhaps make one global crosshair_type cvar which has 0 for disabled, 1 for custom, 2 for per weapon, etc? me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn); diff --git a/qcsrc/menu/xonotic/dialog_settings_audio.c b/qcsrc/menu/xonotic/dialog_settings_audio.c index 1f1ee385b2..174873fe7f 100644 --- a/qcsrc/menu/xonotic/dialog_settings_audio.c +++ b/qcsrc/menu/xonotic/dialog_settings_audio.c @@ -114,12 +114,12 @@ void XonoticAudioSettingsTab_fill(entity me) setDependentStringNotEqual(s, "mastervolume", "0"); me.TR(me); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "menu_snd_attenuation_method", _("New style sound attenuation"))); + me.TD(me, 1, 3, makeXonoticCheckBox(0, "menu_snd_attenuation_method", _("New style sound attenuation"))); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "snd_mutewhenidle", _("Mute sounds when not active"))); + me.TD(me, 1, 3, makeXonoticCheckBox(0, "snd_mutewhenidle", _("Mute sounds when not active"))); me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn); - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Frequency:"))); + me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Frequency:"))); me.TD(me, 1, 2, e = makeXonoticTextSlider("snd_speed")); e.addValue(e, _("8 kHz"), "8000"); e.addValue(e, _("11.025 kHz"), "11025"); @@ -131,7 +131,7 @@ void XonoticAudioSettingsTab_fill(entity me) e.addValue(e, _("48 kHz"), "48000"); e.configureXonoticTextSliderValues(e); me.TR(me); - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Channels:"))); + me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Channels:"))); me.TD(me, 1, 2, e = makeXonoticTextSlider("snd_channels")); e.addValue(e, _("Mono"), "1"); e.addValue(e, _("Stereo"), "2"); @@ -148,33 +148,15 @@ void XonoticAudioSettingsTab_fill(entity me) me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "snd_spatialization_control", _("Headphone friendly mode"))); setDependent(e, "snd_channels", 1.5, 0.5); me.TR(me); - /*me.TR(me); // Samual: I REALLY don't think these are relevant to anyone, and just clutter up the menu pointlessly. The defaults are fine. - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Spatial voices:"))); - me.TD(me, 1, 2/3, e = makeXonoticRadioButton(1, "cl_voice_directional", "0", ZCTX(_("VOCS^None")))); - me.TD(me, 1, 2/3, e = makeXonoticRadioButton(1, "cl_voice_directional", "2", ZCTX(_("VOCS^Taunts")))); - me.TD(me, 1, 2/3, e = makeXonoticRadioButton(1, "cl_voice_directional", "1", ZCTX(_("VOCS^All")))); me.TR(me); - me.TDempty(me, 0.2); - me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Taunt range:"))); - setDependent(e, "cl_voice_directional", 0.5, -0.5); - me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_voice_directional_taunt_attenuation")); - e.addValue(e, ZCTX(_("RNG^Very short")), "3"); - e.addValue(e, ZCTX(_("RNG^Short")), "2"); - e.addValue(e, ZCTX(_("RNG^Normal")), "0.5"); - e.addValue(e, ZCTX(_("RNG^Long")), "0.25"); - e.addValue(e, ZCTX(_("RNG^Full")), "0.015625"); - e.configureXonoticTextSliderValues(e); - setDependent(e, "cl_voice_directional", 0.5, -0.5); - me.TR(me);*/ + me.TD(me, 1, 3, makeXonoticCheckBox(0, "cl_hitsound", _("Hit indication sound"))); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_hitsound", _("Hit indication sound"))); + me.TD(me, 1, 3, makeXonoticCheckBox(0, "con_chatsound", _("Chat message sound"))); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "con_chatsound", _("Chat message sound"))); + me.TD(me, 1, 3, makeXonoticCheckBoxEx(2, 0, "menu_sounds", _("Menu sounds"))); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBoxEx(2, 0, "menu_sounds", _("Menu sounds"))); me.TR(me); - me.TR(me); - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Time announcer:"))); + me.TD(me, 1, 1, makeXonoticTextLabel(0, _("Time announcer:"))); me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_announcer_maptime")); e.addValue(e, ZCTX(_("WRN^Disabled")), "0"); e.addValue(e, _("1 minute"), "1"); @@ -191,13 +173,12 @@ void XonoticAudioSettingsTab_fill(entity me) e.savedValue = 0.65; // default me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Frequency:"))); + me.TD(me, 1, 0.8, makeXonoticTextLabel(0, _("Frequency:"))); me.TD(me, 1, 2, sl); me.TR(me); me.TR(me); if(cvar("developer")) - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "showsound", _("Debug info about sounds"))); - + me.TD(me, 1, 3, makeXonoticCheckBox(0, "showsound", _("Debug info about sounds"))); me.gotoRC(me, me.rows - 1, 0); me.TD(me, 1, me.columns, makeXonoticCommandButton(_("Apply immediately"), '0 0 0', "snd_restart; snd_attenuation_method_$menu_snd_attenuation_method; sendcvar cl_hitsound; sendcvar cl_autotaunt; sendcvar cl_voice_directional; sendcvar cl_voice_directional_taunt_attenuation", COMMANDBUTTON_APPLY)); diff --git a/qcsrc/menu/xonotic/languagelist.c b/qcsrc/menu/xonotic/languagelist.c index 5f6226f493..5be639a075 100644 --- a/qcsrc/menu/xonotic/languagelist.c +++ b/qcsrc/menu/xonotic/languagelist.c @@ -153,6 +153,7 @@ void XonoticLanguageList_getLanguages(entity me) buf = buf_create(); fh = fopen("languages.txt", FILE_READ); + i = 0; while((s = fgets(fh))) { n = tokenize_console(s); diff --git a/qcsrc/menu/xonotic/playermodel.c b/qcsrc/menu/xonotic/playermodel.c index 61eb4a6816..f37aaeb075 100644 --- a/qcsrc/menu/xonotic/playermodel.c +++ b/qcsrc/menu/xonotic/playermodel.c @@ -180,8 +180,13 @@ void XonoticPlayerModelSelector_draw(entity me) SUPER(XonoticPlayerModelSelector).draw(me); // draw text on the image, handle \n in the description + + draw_beginBoldFont(); + draw_CenterText('0.5 0 0', me.currentModelTitle, me.realFontSize * (me.titleFontSize / me.fontSize), SKINCOLOR_MODELTITLE, SKINALPHA_MODELTITLE, FALSE); + draw_endBoldFont(); + o = '0.5 1 0' - eY * me.realFontSize_y * ((n = tokenizebyseparator(me.currentModelDescription, "\n")) + 0.5); for(i = 0; i < n; ++i) { diff --git a/qcsrc/menu/xonotic/serverlist.c b/qcsrc/menu/xonotic/serverlist.c index fbf64c98be..44ca1b2279 100644 --- a/qcsrc/menu/xonotic/serverlist.c +++ b/qcsrc/menu/xonotic/serverlist.c @@ -608,6 +608,7 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float s = gethostcachestring(SLIST_FIELD_QCSTATUS, i); m = tokenizebyseparator(s, ":"); + typestr = ""; if(m >= 2) { typestr = argv(0); @@ -616,6 +617,7 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float freeslots = -1; sflags = -1; modname = ""; + pure = 0; for(j = 2; j < m; ++j) { if(argv(j) == "") @@ -649,6 +651,7 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float if(modname != "Xonotic") if(modname != "MinstaGib") if(modname != "CTS") + if(modname != "NIX") if(modname != "NewToys") pure = 0; diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 78ac9de46c..4c03eaa5f9 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -622,6 +622,8 @@ float GameType_GetID(float cnt) #define GAMETYPE(id) if(i++ == cnt) return id; GAMETYPES #undef GAMETYPE + + unused_float = i; return 0; } diff --git a/qcsrc/menu/xonotic/weaponslist.c b/qcsrc/menu/xonotic/weaponslist.c index 1f06b9d88b..6790befe12 100644 --- a/qcsrc/menu/xonotic/weaponslist.c +++ b/qcsrc/menu/xonotic/weaponslist.c @@ -97,7 +97,10 @@ void XonoticWeaponsList_drawListBoxItem(entity me, float i, vector absSize, floa if(isSelected) draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED); e = get_weaponinfo(stof(argv(i))); - draw_Text(me.realUpperMargin * eY, e.message, me.realFontSize, '1 1 1', SKINALPHA_TEXT, 0); + string msg = e.message; + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + msg = sprintf(_("%s (mutator weapon)"), msg); + draw_Text(me.realUpperMargin * eY, msg, me.realFontSize, '1 1 1', SKINALPHA_TEXT, 0); } float XonoticWeaponsList_keyDown(entity me, float scan, float ascii, float shift) diff --git a/qcsrc/server/accuracy.qc b/qcsrc/server/accuracy.qc index f17593af95..9271e03d0a 100644 --- a/qcsrc/server/accuracy.qc +++ b/qcsrc/server/accuracy.qc @@ -32,10 +32,14 @@ float accuracy_send(entity to, float sf) if(sf == 0) return TRUE; // note: we know that client and server agree about SendFlags... - for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w, f *= 2) + for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) { if(sf & f) WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w]))); + if(f == 0x800000) + f = 1; + else + f *= 2; } return TRUE; } @@ -95,7 +99,7 @@ void accuracy_add(entity e, float w, float fired, float hit) if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]))) return; - w = pow(2, w); + w = pow(2, mod(w, 24)); a.SendFlags |= w; FOR_EACH_CLIENT(a) if(a.classname == "spectator") diff --git a/qcsrc/server/antilag.qc b/qcsrc/server/antilag.qc index 68ec68324d..73025f1f61 100644 --- a/qcsrc/server/antilag.qc +++ b/qcsrc/server/antilag.qc @@ -15,6 +15,9 @@ void antilag_dummy() void antilag_record(entity e, float t) { + if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT) + return; + if(e.vehicle) antilag_record(e.vehicle, t); @@ -92,6 +95,10 @@ vector antilag_takebackavgvelocity(entity e, float t0, float t1) void antilag_takeback(entity e, float t) { + + if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT) + return; + if(e.vehicle) antilag_takeback(e.vehicle, t); @@ -104,6 +111,9 @@ void antilag_takeback(entity e, float t) void antilag_restore(entity e) { + if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT) + return; + if(e.vehicle) antilag_restore(e.vehicle); @@ -122,7 +132,7 @@ void antilag_clear(entity e) for(i = 0; i < ANTILAG_MAX_ORIGINS; ++i) { e.(antilag_times[i]) = -2342; - e.(antilag_origins[i]) = self.origin; + e.(antilag_origins[i]) = e.origin; } e.antilag_index = ANTILAG_MAX_ORIGINS - 1; // next one is 0 } diff --git a/qcsrc/server/attic/bot/havocbot/role_ctf.qc b/qcsrc/server/attic/bot/havocbot/role_ctf.qc new file mode 100644 index 0000000000..0946f43425 --- /dev/null +++ b/qcsrc/server/attic/bot/havocbot/role_ctf.qc @@ -0,0 +1,684 @@ +#define HAVOCBOT_CTF_ROLE_NONE 0 +#define HAVOCBOT_CTF_ROLE_DEFENSE 2 +#define HAVOCBOT_CTF_ROLE_MIDDLE 4 +#define HAVOCBOT_CTF_ROLE_OFFENSE 8 +#define HAVOCBOT_CTF_ROLE_CARRIER 16 +#define HAVOCBOT_CTF_ROLE_RETRIEVER 32 +#define HAVOCBOT_CTF_ROLE_ESCORT 64 + +.void() havocbot_role; +.void() havocbot_previous_role; + +void() havocbot_role_ctf_middle; +void() havocbot_role_ctf_defense; +void() havocbot_role_ctf_offense; +void() havocbot_role_ctf_carrier; +void() havocbot_role_ctf_retriever; +void() havocbot_role_ctf_escort; + +void(entity bot) havocbot_ctf_reset_role; +void(float ratingscale, vector org, float sradius) havocbot_goalrating_items; +void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; + +.float havocbot_cantfindflag; +.float havocbot_role_timeout; +.entity ctf_worldflagnext; +.entity bot_basewaypoint; + +entity ctf_worldflaglist; +vector havocbot_ctf_middlepoint; +float havocbot_ctf_middlepoint_radius; + +entity havocbot_ctf_find_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team == f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +entity havocbot_ctf_find_enemy_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team != f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +float havocbot_ctf_teamcount(entity bot, vector org, float radius) +{ + if not(teamplay) + return 0; + + float c = 0; + entity head; + + FOR_EACH_PLAYER(head) + { + if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot) + continue; + + if(vlen(head.origin - org) < radius) + ++c; + } + + return c; +} + +void havocbot_goalrating_ctf_ourflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourbase(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemyflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team != head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemybase(float ratingscale) +{ + if not(bot_waypoints_for_items) + { + havocbot_goalrating_ctf_enemyflag(ratingscale); + return; + } + + entity head; + + head = havocbot_ctf_find_enemy_flag(self); + + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourstolenflag(float ratingscale) +{ + entity mf; + + mf = havocbot_ctf_find_flag(self); + + if(mf.ctf_status == FLAG_BASE) + return; + + if(mf.tag_entity) + navigation_routerating(mf.tag_entity, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + // flag is out in the field + if(head.ctf_status != FLAG_BASE) + if(head.tag_entity==world) // dropped + { + if(radius) + { + if(vlen(org-head.origin) 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(distance1000) + if(checkpvs(self.origin,closestplayer)||random()<0.5) + havocbot_goalrating_ctf_ourbase(30000); + + havocbot_goalrating_ctf_ourstolenflag(20000); + havocbot_goalrating_ctf_droppedflags(20000, org, radius); + havocbot_goalrating_enemyplayers(15000, org, radius); + havocbot_goalrating_items(10000, org, radius); + havocbot_goalrating_items(5000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +void havocbot_calculate_middlepoint() +{ + entity f; + vector s = '0 0 0'; + vector fo = '0 0 0'; + float n = 0; + + f = ctf_worldflaglist; + while (f) + { + fo = f.origin; + s = s + fo; + f = f.ctf_worldflagnext; + } + if(!n) + return; + havocbot_ctf_middlepoint = s * (1.0 / n); + havocbot_ctf_middlepoint_radius = vlen(fo - havocbot_ctf_middlepoint); +} + +void havocbot_ctf_reset_role(entity bot) +{ + float cdefense, cmiddle, coffense; + entity mf, ef, head; + float c; + + if(bot.deadflag != DEAD_NO) + return; + + if(vlen(havocbot_ctf_middlepoint)==0) + havocbot_calculate_middlepoint(); + + // Check ctf flags + if (bot.flagcarried) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(bot); + ef = havocbot_ctf_find_enemy_flag(bot); + + // Retrieve stolen flag + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + // If enemy flag is taken go to the middle to intercept pursuers + if(ef.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + + // if there is only me on the team switch to offense + c = 0; + FOR_EACH_PLAYER(head) + if(head.team==bot.team) + ++c; + + if(c==1) + { + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); + return; + } + + // Evaluate best position to take + // Count mates on middle position + cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5); + + // Count mates on defense position + cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5); + + // Count mates on offense position + coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius); + + if(cdefense<=coffense) + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE); + else if(coffense<=cmiddle) + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); + else + havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); +} + +void havocbot_chooserole_ctf() +{ + havocbot_ctf_reset_role(self); +} diff --git a/qcsrc/server/attic/bot/havocbot/role_freezetag.qc b/qcsrc/server/attic/bot/havocbot/role_freezetag.qc new file mode 100644 index 0000000000..4e5669eb2e --- /dev/null +++ b/qcsrc/server/attic/bot/havocbot/role_freezetag.qc @@ -0,0 +1,109 @@ +void() havocbot_role_ft_freeing; +void() havocbot_role_ft_offense; + +void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradius) +{ + entity head; + float distance; + + FOR_EACH_PLAYER(head) + { + if ((head != self) && (head.team == self.team)) + { + if (head.freezetag_frozen) + { + distance = vlen(head.origin - org); + if (distance > sradius) + continue; + navigation_routerating(head, ratingscale, 2000); + } + else + { + // If teamate is not frozen still seek them out as fight better + // in a group. + navigation_routerating(head, ratingscale/3, 2000); + } + } + } +} + +void havocbot_role_ft_offense() +{ + entity head; + float unfrozen; + + if(self.deadflag != DEAD_NO) + return; + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + + // Count how many players on team are unfrozen. + unfrozen = 0; + FOR_EACH_PLAYER(head) + { + if ((head.team == self.team) && (!head.freezetag_frozen)) + unfrozen++; + } + + // If only one left on team or if role has timed out then start trying to free players. + if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout)) + { + dprint("changing role to freeing\n"); + self.havocbot_role = havocbot_role_ft_freeing; + self.havocbot_role_timeout = 0; + return; + } + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + havocbot_goalrating_freeplayers(9000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } +} + +void havocbot_role_ft_freeing() +{ + if(self.deadflag != DEAD_NO) + return; + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + + if (time > self.havocbot_role_timeout) + { + dprint("changing role to offense\n"); + self.havocbot_role = havocbot_role_ft_offense; + self.havocbot_role_timeout = 0; + return; + } + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(8000, self.origin, 10000); + havocbot_goalrating_enemyplayers(10000, self.origin, 10000); + havocbot_goalrating_freeplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } +} + +void havocbot_chooserole_ft() +{ + if(self.deadflag != DEAD_NO) + return; + + if (random() < 0.5) + self.havocbot_role = havocbot_role_ft_freeing; + else + self.havocbot_role = havocbot_role_ft_offense; +} diff --git a/qcsrc/server/attic/bot/havocbot/role_keepaway.qc b/qcsrc/server/attic/bot/havocbot/role_keepaway.qc new file mode 100644 index 0000000000..ede6208a16 --- /dev/null +++ b/qcsrc/server/attic/bot/havocbot/role_keepaway.qc @@ -0,0 +1,82 @@ +void() havocbot_role_ka_carrier; +void() havocbot_role_ka_collector; +void() havocbot_chooserole_ka; + +entity ka_ball; + +// Keepaway +// If you don't have the ball, get it; if you do, kill people. + +void havocbot_goalrating_ball(float ratingscale, vector org) +{ + float t; + entity ball_owner; + ball_owner = ka_ball.owner; + + if (ball_owner == self) + return; + + // If ball is carried by player then hunt them down. + if (ball_owner) + { + t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); + navigation_routerating(ball_owner, t * ratingscale, 2000); + } + + // Ball has been dropped so collect. + navigation_routerating(ka_ball, ratingscale, 2000); +} + +void havocbot_role_ka_carrier() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } + + if (!self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_collector; + self.bot_strategytime = 0; + } +} + +void havocbot_role_ka_collector() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(1000, self.origin, 10000); + havocbot_goalrating_ball(20000, self.origin); + navigation_goalrating_end(); + } + + if (self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_carrier; + self.bot_strategytime = 0; + } +} + +void havocbot_chooserole_ka() +{ + if (self.ballcarried) + self.havocbot_role = havocbot_role_ka_carrier; + else + self.havocbot_role = havocbot_role_ka_collector; +} diff --git a/qcsrc/server/attic/ctf.qc b/qcsrc/server/attic/ctf.qc new file mode 100644 index 0000000000..d65d866ca6 --- /dev/null +++ b/qcsrc/server/attic/ctf.qc @@ -0,0 +1,1226 @@ +#define FLAG_MIN (PL_MIN + '0 0 -13') +#define FLAG_MAX (PL_MAX + '0 0 -13') + +.entity basewaypoint; +.entity sprite; +entity ctf_worldflaglist; // CTF flags in the map +.entity ctf_worldflagnext; +.float dropperid; +.float ctf_droptime; + +.float next_take_time; // the next time a player can pick up a flag (time + blah) + /// I used this, in part, to fix the looping score bug. - avirox +//float FLAGSCORE_PICKUP = 1; +//float FLAGSCORE_RETURN = 5; // returned by owner team +//float FLAGSCORE_RETURNROGUE = 10; // returned by rogue team +//float FLAGSCORE_CAPTURE = 5; + +#define FLAG_CARRY_POS '-15 0 7' + +.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture + +float captureshield_min_negscore; // punish at -20 points +float captureshield_max_ratio; // punish at most 30% of each team +float captureshield_force; // push force of the shield + +float ctf_captureshield_shielded(entity p) +{ + float s, se; + entity e; + float players_worseeq, players_total; + + if(captureshield_max_ratio <= 0) + return FALSE; + + s = PlayerScore_Add(p, SP_SCORE, 0); + if(s >= -captureshield_min_negscore) + return FALSE; + + players_total = players_worseeq = 0; + FOR_EACH_PLAYER(e) + { + if(e.team != p.team) + continue; + se = PlayerScore_Add(e, SP_SCORE, 0); + if(se <= s) + ++players_worseeq; + ++players_total; + } + + // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse + // use this rule here + + if(players_worseeq >= players_total * captureshield_max_ratio) + return FALSE; + + return TRUE; +} + +void ctf_captureshield_update(entity p, float dir) +{ + float should; + if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only + { + should = ctf_captureshield_shielded(p); + if(should != dir) + { + if(should) + { + Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0); + // TODO csqc notifier for this + } + else + { + Send_CSQC_Centerprint_Generic(p, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0); + // TODO csqc notifier for this + } + p.ctf_captureshielded = should; + } + } +} + +float ctf_captureshield_customize() +{ + if not(other.ctf_captureshielded) + return FALSE; + if(self.team == other.team) + return FALSE; + return TRUE; +} + +.float ctf_captureshield_touch_msgtime; +void ctf_captureshield_touch() +{ + if not(other.ctf_captureshielded) + return; + if(self.team == other.team) + return; + vector mymid; + vector othermid; + mymid = (self.absmin + self.absmax) * 0.5; + othermid = (other.absmin + other.absmax) * 0.5; + Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force); + if (time - other.ctf_captureshield_touch_msgtime > 2) + Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0); + other.ctf_captureshield_touch_msgtime = time; +} + +void ctf_flag_spawnstuff() +{ + entity e; + e = spawn(); + e.enemy = self; + e.team = self.team; + e.touch = ctf_captureshield_touch; + e.customizeentityforclient = ctf_captureshield_customize; + e.classname = "ctf_captureshield"; + e.effects = EF_ADDITIVE; + e.movetype = MOVETYPE_NOCLIP; + e.solid = SOLID_TRIGGER; + e.avelocity = '7 0 11'; + setorigin(e, self.origin); + setmodel(e, "models/ctf/shield.md3"); + e.scale = 0.5; + setsize(e, e.scale * e.mins, e.scale * e.maxs); + + waypoint_spawnforitem_force(self, self.origin); + self.nearestwaypointtimeout = 0; // activate waypointing again + self.basewaypoint = self.nearestwaypoint; + + if(self.team == COLOR_TEAM1) + WaypointSprite_SpawnFixed("redbase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM1 - 1, FALSE)); + else + WaypointSprite_SpawnFixed("bluebase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM2 - 1, FALSE)); +} + +float ctf_score_value(string parameter) +{ + return cvar(strcat("g_ctf_personal", parameter)); +} + +void FakeTimeLimit(entity e, float t) +{ + msg_entity = e; + WriteByte(MSG_ONE, 3); // svc_updatestat + WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT + if(t < 0) + WriteCoord(MSG_ONE, autocvar_timelimit); + else + WriteCoord(MSG_ONE, (t + 1) / 60); +} + +float flagcaptimerecord; +.float flagpickuptime; +//.float iscommander; +//.float ctf_state; + +void() FlagThink; +void() FlagTouch; + +void place_flag() +{ + if(self.classname != "item_flag_team") + { + backtrace("PlaceFlag a non-flag"); + return; + } + + setattachment(self, world, ""); + self.mdl = self.model; + self.flags = FL_ITEM | FL_NOTARGET; + self.solid = SOLID_TRIGGER; + self.movetype = MOVETYPE_NONE; + self.velocity = '0 0 0'; + self.origin_z = self.origin_z + 6; + self.think = FlagThink; + self.touch = FlagTouch; + self.nextthink = time + 0.1; + self.cnt = FLAG_BASE; + self.mangle = self.angles; + self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + //self.effects = self.effects | EF_DIMLIGHT; + if(self.noalign) + { + self.dropped_origin = self.origin; + } + else + { + droptofloor(); + self.movetype = MOVETYPE_TOSS; + } + + InitializeEntity(self, ctf_flag_spawnstuff, INITPRIO_SETLOCATION); +} + +void LogCTF(string mode, float flagteam, entity actor) +{ + string s; + if(!autocvar_sv_eventlog) + return; + s = strcat(":ctf:", mode); + s = strcat(s, ":", ftos(flagteam)); + if(actor != world) + s = strcat(s, ":", ftos(actor.playerid)); + GameLogEcho(s); +} + +void RegenFlag(entity e) +{ + if(e.classname != "item_flag_team") + { + backtrace("RegenFlag a non-flag"); + return; + } + + if(e.waypointsprite_attachedforcarrier) + WaypointSprite_DetachCarrier(e); + + setattachment(e, world, ""); + e.damageforcescale = 0; + e.takedamage = DAMAGE_NO; + e.movetype = MOVETYPE_NONE; + if(!e.noalign) + e.movetype = MOVETYPE_TOSS; + e.velocity = '0 0 0'; + e.solid = SOLID_TRIGGER; + // TODO: play a sound here + setorigin(e, e.dropped_origin); + e.angles = e.mangle; + e.cnt = FLAG_BASE; + e.owner = world; + e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk +} + +void ReturnFlag(entity e) +{ + if(e.classname != "item_flag_team") + { + backtrace("ReturnFlag a non-flag"); + return; + } + + if (e.owner) + if (e.owner.flagcarried == e) + { + WaypointSprite_DetachCarrier(e.owner); + e.owner.flagcarried = world; + + if(e.speedrunning) + FakeTimeLimit(e.owner, -1); + } + e.owner = world; + RegenFlag(e); +} + +void DropFlag(entity e, entity penalty_receiver, entity attacker) +{ + entity p; + + if(e.classname != "item_flag_team") + { + backtrace("DropFlag a non-flag"); + return; + } + + if(e.speedrunning) + { + ReturnFlag(e); + return; + } + + if (!e.owner) + { + dprint("FLAG: drop - no owner?!?!\n"); + return; + } + p = e.owner; + if (p.flagcarried != e) + { + dprint("FLAG: drop - owner is not carrying this flag??\n"); + return; + } + //bprint(p.netname, "^7 lost the ", e.netname, "\n"); + Send_KillNotification (p.netname, e.netname, "", INFO_LOSTFLAG, MSG_INFO); + + if(penalty_receiver) + UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop")); + else + UpdateFrags(p, -ctf_score_value("penalty_drop")); + PlayerScore_Add(p, SP_CTF_DROPS, +1); + ctf_captureshield_update(p, 0); // shield only + e.playerid = attacker.playerid; + e.ctf_droptime = time; + WaypointSprite_Spawn("flagdropped", 0, 0, e, '0 0 1' * 61, world, COLOR_TEAM1 + COLOR_TEAM2 - e.team, e, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAG, '0 1 1'); + WaypointSprite_Ping(e.waypointsprite_attachedforcarrier); + + if(p.waypointsprite_attachedforcarrier) + { + WaypointSprite_DetachCarrier(p); + } + else + { + bprint("\{1}^1Flag carrier had no flag sprite?!?\n"); + backtrace("Flag carrier had no flag sprite?!?"); + } + LogCTF("dropped", p.team, p); + sound (p, CH_TRIGGER, self.noise4, VOL_BASE, ATTN_NONE); + + setattachment(e, world, ""); + e.damageforcescale = autocvar_g_balance_ctf_damageforcescale; + e.takedamage = DAMAGE_YES; + + if (p.flagcarried == e) + p.flagcarried = world; + e.owner = world; + + e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk + e.solid = SOLID_TRIGGER; + e.movetype = MOVETYPE_TOSS; + // setsize(e, '-16 -16 0', '16 16 74'); + setorigin(e, p.origin - '0 0 24' + '0 0 37'); + e.cnt = FLAG_DROPPED; + e.velocity = '0 0 300'; + e.pain_finished = time + autocvar_g_ctf_flag_returntime;//30; + + trace_startsolid = FALSE; + tracebox(e.origin, e.mins, e.maxs, e.origin, TRUE, e); + if(trace_startsolid) + dprint("FLAG FALLTHROUGH will happen SOON\n"); +} + +void FlagThink() +{ + entity e; + + self.nextthink = time + 0.1; + + // sorry, we have to reset the flag size if it got squished by something + if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) + { + // if we can grow back, grow back + tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self); + if(!trace_startsolid) + setsize(self, FLAG_MIN, FLAG_MAX); + } + + if(self == ctf_worldflaglist) // only for the first flag + { + FOR_EACH_CLIENT(e) + ctf_captureshield_update(e, 1); // release shield only + } + + if(self.speedrunning) + if(self.cnt == FLAG_CARRY) + { + if(self.owner) + if(flagcaptimerecord) + if(time >= self.flagpickuptime + flagcaptimerecord) + { + bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n"); + + sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE); + self.owner.impulse = 141; // returning! + + e = self; + self = self.owner; + ReturnFlag(e); + ImpulseCommands(); + self = e; + return; + } + } + + if (self.cnt == FLAG_BASE) + return; + + if (self.cnt == FLAG_DROPPED) + { + // flag fallthrough? FIXME remove this if bug is really fixed now + if(self.origin_z < -131072) + { + dprint("FLAG FALLTHROUGH just happened\n"); + self.pain_finished = 0; + } + setattachment(self, world, ""); + if (time > self.pain_finished) + { + bprint("The ", self.netname, " has returned to base\n"); + sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE); + LogCTF("returned", self.team, world); + ReturnFlag(self); + } + return; + } + + e = self.owner; + if (e.classname != "player" || (e.deadflag) || (e.flagcarried != self)) + { + dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n"); + DropFlag(self, world, world); + return; + } +} + +float ctf_usekey() +{ + if(self.flagcarried) + { + DropFlag(self.flagcarried, self, world); + return TRUE; + } + return FALSE; +} + +void flag_cap_ring_spawn(vector org) +{ + shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1); +} + +// TODO add FlagDamage, replace weird hurttrigger handling inside trigger_hurt code by it +void FlagTouch() +{ + if(gameover) return; + + float t; + entity player; + string s, s0, h0, h1; + + if (self.cnt == FLAG_CARRY) + return; + + if (self.cnt == FLAG_DROPPED) + { + if(ITEM_TOUCH_NEEDKILL()) + { + self.pain_finished = 0; // return immediately + return; + } + } + + if (other.classname != "player") + return; + if (other.health < 1) // ignore dead players + return; + + if (self.cnt == FLAG_BASE) + if (other.team == self.team) + if (other.flagcarried) // he's got a flag + if (other.flagcarried.team != self.team) // capture + { + if (other.flagcarried == world) + { + return; + } + if(autocvar_g_ctf_captimerecord_always || player_count - currentbots <= 1) // at most one human + { + t = time - other.flagcarried.flagpickuptime; + s = ftos_decimals(t, 2); + s0 = ftos_decimals(flagcaptimerecord, 2); + h0 = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname")); + h1 = other.netname; + if(h0 == h1) + h0 = "their"; + else + h0 = strcat(h0, "^7's"); // h0: display text for previous netname + if (flagcaptimerecord == 0) + { + s = strcat(" in ", s, " seconds"); + flagcaptimerecord = t; + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t)); + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1); + write_recordmarker(other, time - t, t); + } + else if (t < flagcaptimerecord) + { + s = strcat(" in ", s, " seconds, breaking ", h0, " previous record of ", s0, " seconds"); + flagcaptimerecord = t; + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t)); + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1); + write_recordmarker(other, time - t, t); + } + else + { + s = strcat(" in ", s, " seconds, failing to break ", h0, " record of ", s0, " seconds"); + } + } + else + s = ""; + + Send_KillNotification (other.netname, other.flagcarried.netname, s, INFO_CAPTUREFLAG, MSG_INFO); + + PlayerTeamScore_Add(other, SP_CTF_CAPS, ST_CTF_CAPS, 1); + LogCTF("capture", other.flagcarried.team, other); + // give credit to the individual player + UpdateFrags(other, ctf_score_value("score_capture")); + + if (autocvar_g_ctf_flag_capture_effects) { + if (other.team == COLOR_TEAM1) { // red team scores effect + pointparticles(particleeffectnum("red_ground_quake"), self.origin, '0 0 0', 1); + flag_cap_ring_spawn(self.origin); + } + if (other.team == COLOR_TEAM2) { // blue team scores effect + pointparticles(particleeffectnum("blue_ground_quake"), self.origin, '0 0 0', 1); + flag_cap_ring_spawn(self.origin); + } + } + + sound (other, CH_TRIGGER, self.noise2, VOL_BASE, ATTN_NONE); + WaypointSprite_DetachCarrier(other); + if(self.speedrunning) + FakeTimeLimit(other, -1); + RegenFlag (other.flagcarried); + other.flagcarried = world; + other.next_take_time = time + 1; + } + if (self.cnt == FLAG_BASE) + if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2) // only red and blue team can steal flags + if (other.team != self.team) + if (!other.flagcarried) + if (!other.ctf_captureshielded) + { + if(MUTATOR_CALLHOOK(ItemTouch)) + return; + + if (other.next_take_time > time) + return; + + if (autocvar_g_ctf_flag_pickup_effects) // pickup effect + pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1); + + // pick up + self.flagpickuptime = time; // used for timing runs + self.speedrunning = other.speedrunning; // if speedrunning, flag will self-return and teleport the owner back after the record + if(other.speedrunning) + if(flagcaptimerecord) + FakeTimeLimit(other, time + flagcaptimerecord); + self.solid = SOLID_NOT; + setorigin(self, self.origin); // relink + self.owner = other; + other.flagcarried = self; + self.cnt = FLAG_CARRY; + self.angles = '0 0 0'; + //bprint(other.netname, "^7 got the ", self.netname, "\n"); + Send_KillNotification (other.netname, self.netname, "", INFO_GOTFLAG, MSG_INFO); + UpdateFrags(other, ctf_score_value("score_pickup_base")); + self.dropperid = other.playerid; + PlayerScore_Add(other, SP_CTF_PICKUPS, 1); + LogCTF("steal", self.team, other); + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NONE); + + FOR_EACH_PLAYER(player) + if(player.team == self.team) + centerprint(player, "The enemy got your flag! Retrieve it!"); + + self.movetype = MOVETYPE_NONE; + setorigin(self, FLAG_CARRY_POS); + setattachment(self, other, ""); + WaypointSprite_AttachCarrier("flagcarrier", other, RADARICON_FLAGCARRIER, '1 1 0'); + WaypointSprite_Ping(self.sprite); + + return; + } + + if (self.cnt == FLAG_DROPPED) + { + self.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk + if (other.team == self.team || (other.team != COLOR_TEAM1 && other.team != COLOR_TEAM2)) + { + // return flag + Send_KillNotification (other.netname, self.netname, "", INFO_RETURNFLAG, MSG_INFO); + //bprint(other.netname, "^7 returned the ", self.netname, "\n"); + + // punish the player who last had it + FOR_EACH_PLAYER(player) + if(player.playerid == self.dropperid) + { + PlayerScore_Add(player, SP_SCORE, -ctf_score_value("penalty_returned")); + ctf_captureshield_update(player, 0); // shield only + } + + // punish the team who was last carrying it + if(self.team == COLOR_TEAM1) + TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, -ctf_score_value("penalty_returned")); + else + TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, -ctf_score_value("penalty_returned")); + + // reward the player who returned it + if(other.playerid == self.playerid) // is this the guy who killed the FC last? + { + if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2) + UpdateFrags(other, ctf_score_value("score_return_by_killer")); + else + UpdateFrags(other, ctf_score_value("score_return_rogue_by_killer")); + } + else + { + if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2) + UpdateFrags(other, ctf_score_value("score_return")); + else + UpdateFrags(other, ctf_score_value("score_return_rogue")); + } + PlayerScore_Add(other, SP_CTF_RETURNS, 1); + LogCTF("return", self.team, other); + sound (other, CH_TRIGGER, self.noise1, VOL_BASE, ATTN_NONE); + ReturnFlag(self); + } + else if (!other.flagcarried && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_balance_ctf_delay_collect)) + { + if(self.waypointsprite_attachedforcarrier) + WaypointSprite_DetachCarrier(self); + + if (autocvar_g_ctf_flag_pickup_effects) // field pickup effect + pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1); + + // pick up + self.solid = SOLID_NOT; + setorigin(self, self.origin); // relink + self.owner = other; + other.flagcarried = self; + self.cnt = FLAG_CARRY; + Send_KillNotification (other.netname, self.netname, "", INFO_PICKUPFLAG, MSG_INFO); + //bprint(other.netname, "^7 picked up the ", self.netname, "\n"); + + float f; + f = bound(0, (self.pain_finished - time) / autocvar_g_ctf_flag_returntime, 1); + //print("factor is ", ftos(f), "\n"); + f = ctf_score_value("score_pickup_dropped_late") * (1-f) + + ctf_score_value("score_pickup_dropped_early") * f; + f = floor(f + 0.5); + self.dropperid = other.playerid; + //print("score is ", ftos(f), "\n"); + + UpdateFrags(other, f); + PlayerScore_Add(other, SP_CTF_PICKUPS, 1); + LogCTF("pickup", self.team, other); + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NONE); + + FOR_EACH_PLAYER(player) + if(player.team == self.team) + centerprint(player, "The enemy got your flag! Retrieve it!"); + + self.movetype = MOVETYPE_NONE; // flag must have MOVETYPE_NONE here, otherwise it will drop through the floor... + setorigin(self, FLAG_CARRY_POS); + setattachment(self, other, ""); + self.damageforcescale = 0; + self.takedamage = DAMAGE_NO; + WaypointSprite_AttachCarrier("flagcarrier", other, RADARICON_FLAGCARRIER, '1 1 0'); + } + } +} + +/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player +in team one (Red). + +Keys: +"angle" + viewing angle when spawning +*/ +void spawnfunc_info_player_team1() +{ + if(g_assault) + { + remove(self); + return; + } + self.team = COLOR_TEAM1; // red + spawnfunc_info_player_deathmatch(); +} +//self.team = 4;self.classname = "info_player_start";spawnfunc_info_player_start();} + +/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in +team two (Blue). + +Keys: +"angle" + viewing angle when spawning +*/ +void spawnfunc_info_player_team2() +{ + if(g_assault) + { + remove(self); + return; + } + self.team = COLOR_TEAM2; // blue + spawnfunc_info_player_deathmatch(); +} +//self.team = 13;self.classname = "info_player_start";spawnfunc_info_player_start();} + +/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in +team three (Yellow). + +Keys: +"angle" + viewing angle when spawning +*/ +void spawnfunc_info_player_team3() +{ + if(g_assault) + { + remove(self); + return; + } + self.team = COLOR_TEAM3; // yellow + spawnfunc_info_player_deathmatch(); +} + + +/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in +team four (Magenta). + +Keys: +"angle" + viewing angle when spawning +*/ +void spawnfunc_info_player_team4() +{ + if(g_assault) + { + remove(self); + return; + } + self.team = COLOR_TEAM4; // purple + spawnfunc_info_player_deathmatch(); +} + +void item_flag_reset() +{ + DropFlag(self, world, world); + if(self.waypointsprite_attachedforcarrier) + WaypointSprite_DetachCarrier(self); + ReturnFlag(self); +} + +void item_flag_postspawn() +{ // Check CTF Item Flag Post Spawn + + // Flag Glow Trail Support + if(autocvar_g_ctf_flag_glowtrails) + { // Provide Flag Glow Trail + if(self.team == COLOR_TEAM1) + // Red + self.glow_color = 251; + else + if(self.team == COLOR_TEAM2) + // Blue + self.glow_color = 210; + + self.glow_size = 25; + self.glow_trail = 1; + } +} + +/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team one (Red). +Multiple are allowed. + +Keys: +"angle" + Angle the flag will point +(minus 90 degrees) +"model" + model to use, note this needs red and blue as skins 0 and 1 + (default models/ctf/flag.md3) +"noise" + sound played when flag is picked up + (default ctf/take.wav) +"noise1" + sound played when flag is returned by a teammate + (default ctf/return.wav) +"noise2" + sound played when flag is captured + (default ctf/redcapture.wav) +"noise3" + sound played when flag is lost in the field and respawns itself + (default ctf/respawn.wav) +*/ + +void spawnfunc_item_flag_team2(); +void spawnfunc_item_flag_team1() +{ + if (!g_ctf) + { + remove(self); + return; + } + + if (g_ctf_reverse) + { + float old_g_ctf_reverse = g_ctf_reverse; + g_ctf_reverse = 0; // avoid an endless loop + spawnfunc_item_flag_team2(); + g_ctf_reverse = old_g_ctf_reverse; + return; + } + + // link flag into ctf_worldflaglist + self.ctf_worldflagnext = ctf_worldflaglist; + ctf_worldflaglist = self; + + self.classname = "item_flag_team"; + self.team = COLOR_TEAM1; // color 4 team (red) + self.items = IT_KEY2; // gold key (redish enough) + self.netname = "^1RED^7 flag"; + self.target = "###item###"; + self.skin = autocvar_g_ctf_flag_red_skin; + if(self.spawnflags & 1) + self.noalign = 1; + if (!self.model) + self.model = autocvar_g_ctf_flag_red_model; + if (!self.noise) + self.noise = "ctf/red_taken.wav"; + if (!self.noise1) + self.noise1 = "ctf/red_returned.wav"; + if (!self.noise2) + self.noise2 = "ctf/red_capture.wav"; // blue team scores by capturing the red flag + if (!self.noise3) + self.noise3 = "ctf/flag_respawn.wav"; + if (!self.noise4) + self.noise4 = "ctf/red_dropped.wav"; + precache_model (self.model); + setmodel (self, self.model); // precision set below + precache_sound (self.noise); + precache_sound (self.noise1); + precache_sound (self.noise2); + precache_sound (self.noise3); + precache_sound (self.noise4); + //setsize(self, '-16 -16 -37', '16 16 37'); + setsize(self, FLAG_MIN, FLAG_MAX); + setorigin(self, self.origin + '0 0 37'); + self.nextthink = time + 0.2; // start after doors etc + self.think = place_flag; + + if(!self.scale) + self.scale = 0.6; + //if(!self.glow_size) + // self.glow_size = 50; + + self.effects = self.effects | EF_LOWPRECISION; + if(autocvar_g_ctf_fullbrightflags) + self.effects |= EF_FULLBRIGHT; + if(autocvar_g_ctf_dynamiclights) + self.effects |= EF_RED; + + // From Spidflisk + item_flag_postspawn(); + + precache_model("models/ctf/shield.md3"); + precache_model("models/ctf/shockwavetransring.md3"); + + self.reset = item_flag_reset; +} + +/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -24) (48 48 64) +CTF flag for team two (Blue). +Multiple are allowed. + +Keys: +"angle" + Angle the flag will point +(minus 90 degrees) +"model" + model to use, note this needs red and blue as skins 0 and 1 + (default models/ctf/flag.md3) +"noise" + sound played when flag is picked up + (default ctf/take.wav) +"noise1" + sound played when flag is returned by a teammate + (default ctf/return.wav) +"noise2" + sound played when flag is captured + (default ctf/bluecapture.wav) +"noise3" + sound played when flag is lost in the field and respawns itself + (default ctf/respawn.wav) +*/ + +void spawnfunc_item_flag_team2() +{ + if (!g_ctf) + { + remove(self); + return; + } + + if (g_ctf_reverse) + { + float old_g_ctf_reverse = g_ctf_reverse; + g_ctf_reverse = 0; // avoid an endless loop + spawnfunc_item_flag_team1(); + g_ctf_reverse = old_g_ctf_reverse; + return; + } + + // link flag into ctf_worldflaglist + self.ctf_worldflagnext = ctf_worldflaglist; + ctf_worldflaglist = self; + + self.classname = "item_flag_team"; + self.team = COLOR_TEAM2; // color 13 team (blue) + self.items = IT_KEY1; // silver key (bluish enough) + self.netname = "^4BLUE^7 flag"; + self.target = "###item###"; + self.skin = autocvar_g_ctf_flag_blue_skin; + if(self.spawnflags & 1) + self.noalign = 1; + if (!self.model) + self.model = autocvar_g_ctf_flag_blue_model; + if (!self.noise) + self.noise = "ctf/blue_taken.wav"; + if (!self.noise1) + self.noise1 = "ctf/blue_returned.wav"; + if (!self.noise2) + self.noise2 = "ctf/blue_capture.wav"; // blue team scores by capturing the red flag + if (!self.noise3) + self.noise3 = "ctf/flag_respawn.wav"; + if (!self.noise4) + self.noise4 = "ctf/blue_dropped.wav"; + precache_model (self.model); + setmodel (self, self.model); // precision set below + precache_sound (self.noise); + precache_sound (self.noise1); + precache_sound (self.noise2); + precache_sound (self.noise3); + precache_sound (self.noise4); + //setsize(self, '-16 -16 -37', '16 16 37'); + setsize(self, FLAG_MIN, FLAG_MAX); + setorigin(self, self.origin + '0 0 37'); + self.nextthink = time + 0.2; // start after doors etc + self.think = place_flag; + + if(!self.scale) + self.scale = 0.6; + //if(!self.glow_size) + // self.glow_size = 50; + + self.effects = self.effects | EF_LOWPRECISION; + if(autocvar_g_ctf_fullbrightflags) + self.effects |= EF_FULLBRIGHT; + if(autocvar_g_ctf_dynamiclights) + self.effects |= EF_BLUE; + + // From Spidflisk + item_flag_postspawn(); + + precache_model("models/ctf/shield.md3"); + precache_model("models/ctf/shockwavetransring.md3"); + + self.reset = item_flag_reset; +} + + +/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32) +Team declaration for CTF gameplay, this allows you to decide what team +names and control point models are used in your map. + +Note: If you use spawnfunc_ctf_team entities you must define at least 2! However, unlike +domination, you don't need to make a blank one too. + +Keys: +"netname" + Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc) +"cnt" + Scoreboard color of the team (for example 4 is red and 13 is blue) + +*/ + +void spawnfunc_ctf_team() +{ + if (!g_ctf) + { + remove(self); + return; + } + self.classname = "ctf_team"; + self.team = self.cnt + 1; +} + +// code from here on is just to support maps that don't have control point and team entities +void ctf_spawnteam (string teamname, float teamcolor) +{ + entity oldself; + oldself = self; + self = spawn(); + self.classname = "ctf_team"; + self.netname = teamname; + self.cnt = teamcolor; + + spawnfunc_ctf_team(); + + self = oldself; +} + +// spawn some default teams if the map is not set up for ctf +void ctf_spawnteams() +{ + float numteams; + + numteams = 2;//cvar("g_ctf_default_teams"); + + ctf_spawnteam("Red", COLOR_TEAM1 - 1); + ctf_spawnteam("Blue", COLOR_TEAM2 - 1); +} + +void ctf_delayedinit() +{ + // if no teams are found, spawn defaults + if (find(world, classname, "ctf_team") == world) + ctf_spawnteams(); + + ScoreRules_ctf(); +} + +void ctf_init() +{ + InitializeEntity(world, ctf_delayedinit, INITPRIO_GAMETYPE); + flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"))); + + captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore; + captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio; + captureshield_force = autocvar_g_ctf_shield_force; +} + +void ctf_setstatus2(entity flag, float shift) +{ + if (flag.cnt == FLAG_CARRY) + if (flag.owner == self) + self.items |= shift * 3; + else + self.items |= shift * 1; + else if (flag.cnt == FLAG_DROPPED) + self.items |= shift * 2; + else + { + // no status bits + } +} + +void ctf_setstatus() +{ + self.items &~= IT_RED_FLAG_TAKEN; + self.items &~= IT_RED_FLAG_LOST; + self.items &~= IT_BLUE_FLAG_TAKEN; + self.items &~= IT_BLUE_FLAG_LOST; + self.items &~= IT_CTF_SHIELDED; + + entity flag; + float redflags, blueflags; + + if(self.ctf_captureshielded) + self.items |= IT_CTF_SHIELDED; + + redflags = 0; + blueflags = 0; + + for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE) + { + if(flag.items & IT_KEY2) // blue + ++redflags; + else if(flag.items & IT_KEY1) // red + ++blueflags; + } + + // blinking magic: if there is more than one flag, show one of these in a clever way + if(redflags) + redflags = mod(floor(time * redflags * 0.75), redflags); + if(blueflags) + blueflags = mod(floor(time * blueflags * 0.75), blueflags); + + for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE) + { + if(flag.items & IT_KEY2) // blue + { + if(--redflags == -1) // happens exactly once (redflags is in 0..count-1, and will --'ed count times) + ctf_setstatus2(flag, IT_RED_FLAG_TAKEN); + } + else if(flag.items & IT_KEY1) // red + { + if(--blueflags == -1) // happens exactly once + ctf_setstatus2(flag, IT_BLUE_FLAG_TAKEN); + } + } +} +/* +entity ctf_team_has_commander(float cteam) +{ + entity pl; + if(cteam != COLOR_TEAM1 || cteam != COLOR_TEAM2) + return world; + + FOR_EACH_REALPLAYER(pl) { + if(pl.team == cteam && pl.iscommander) { + return pl; + } + } + return world; +} + +void ctf_setstate(entity e, float st) +{ + e.ctf_state = st; + ++e.version; +} + +void ctf_new_commander(float cteam) +{ + entity pl, plmax; + + plmax = world; + FOR_EACH_REALPLAYER(pl) { + if(pl.team == cteam) { + if(pl.iscommander) { // don't reassign if alreay there + return; + } + if(plmax == world || plmax.frags < pl.frags) <<<<<<<<<<<<<<<<< BROKEN in new scoring system + plmax = pl; + } + } + if(plmax == world) { + bprint(strcat(ColoredTeamName(cteam), " Team has no Commander!\n")); + return; + } + + plmax.iscommander = TRUE; + ctf_setstate(plmax, 3); + sprint(plmax, "^3You're the commander now!\n"); + centerprint(plmax, "^3You're the commander now!\n"); +} + +void ctf_clientconnect() +{ + self.iscommander = FALSE; + + if(!self.team || self.classname != "player") { + ctf_setstate(self, -1); + } else + ctf_setstate(self, 0); + + self.team_saved = self.team; + + if(self.team != 0 && self.classname == "player" && !ctf_team_has_commander(self.team)) { + ctf_new_commander(self.team); + } +} + +void ctf_playerchanged() +{ + if(!self.team || self.classname != "player") { + ctf_setstate(self, -1); + } else if(self.ctf_state < 0 && self.classname == "player") { + ctf_setstate(self, 0); + } + + if(self.iscommander && + (self.classname != "player" || self.team != self.team_saved) + ) + { + self.iscommander = FALSE; + if(self.classname == "player") + ctf_setstate(self, 0); + else + ctf_setstate(self, -1); + ctf_new_commander(self.team_saved); + } + + self.team_saved = self.team; + + ctf_new_commander(self.team); +} + +void ctf_clientdisconnect() +{ + if(self.iscommander) + { + ctf_new_commander(self.team); + } +} + +entity GetPlayer(string); +float ctf_clientcommand() +{ + entity e; + if(argv(0) == "order") { + if(!g_ctf) { + sprint(self, "This command is not supported in this gamemode.\n"); + return TRUE; + } + if(!self.iscommander) { + sprint(self, "^1You are not the commander!\n"); + return TRUE; + } + if(argv(2) == "") { + sprint(self, "Usage: order #player status - (playernumber as in status)\n"); + return TRUE; + } + e = GetPlayer(argv(1)); + if(e == world) { + sprint(self, "Invalid player.\nUsage: order #player status - (playernumber as in status)\n"); + return TRUE; + } + if(e.team != self.team) { + sprint(self, "^3You can only give orders to your own team!\n"); + return TRUE; + } + if(argv(2) == "attack") { + sprint(self, strcat("Ordering ", e.netname, " to attack!\n")); + sprint(e, "^1Attack!\n"); + centerprint(e, "^7You've been ordered to^9\n^1Attack!\n"); + ctf_setstate(e, 1); + } else if(argv(2) == "defend") { + sprint(self, strcat("Ordering ", e.netname, " to defend!\n")); + sprint(e, "^Defend!\n"); + centerprint(e, "^7You've been ordered to^9\n^2Defend!\n"); + ctf_setstate(e, 2); + } else { + sprint(self, "^7Invalid command, use ^3attack^7, or ^3defend^7.\n"); + } + return TRUE; + } + return FALSE; +} +*/ diff --git a/qcsrc/server/attic/monsters/m_monsters.qc b/qcsrc/server/attic/monsters/m_monsters.qc index 3e160d97b5..b450f761e3 100644 --- a/qcsrc/server/attic/monsters/m_monsters.qc +++ b/qcsrc/server/attic/monsters/m_monsters.qc @@ -259,6 +259,7 @@ void() walkmonster_start_go = // spread think times so they don't all happen at same time self.nextthink = self.nextthink + random()*0.5 + 0.1; self.iscreature = TRUE; + self.teleportable = TELEPORT_NORMAL; self.damagedbycontents = TRUE; force_retouch = 2; // mainly to detect teleports @@ -353,6 +354,7 @@ void() flymonster_start_go = } } self.iscreature = TRUE; + self.teleportable = TELEPORT_NORMAL; self.damagedbycontents = TRUE; force_retouch = 2; // mainly to detect teleports @@ -442,6 +444,7 @@ void() swimmonster_start_go = } } self.iscreature = TRUE; + self.teleportable = TELEPORT_NORMAL; self.damagedbycontents = TRUE; force_retouch = 2; // mainly to detect teleports diff --git a/qcsrc/server/attic/nexball.qc b/qcsrc/server/attic/nexball.qc index a068a33a54..d3f4b55f4a 100644 --- a/qcsrc/server/attic/nexball.qc +++ b/qcsrc/server/attic/nexball.qc @@ -28,6 +28,9 @@ float nb_teams; .float teamtime; +.float nb_dropperid; +.float nb_droptime; + void nb_delayedinit(); void nb_init() // Called early (worldspawn stage) { @@ -155,7 +158,7 @@ void GiveBall (entity plyr, entity ball) ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it ball.team = plyr.team; plyr.ballcarried = ball; - ball.dropperid = plyr.playerid; + ball.nb_dropperid = plyr.playerid; plyr.effects |= g_nexball_basketball_effects_default; ball.effects &~= g_nexball_basketball_effects_default; @@ -188,7 +191,7 @@ void DropBall (entity ball, vector org, vector vel) ball.flags &~= FL_ONGROUND; ball.scale = ball_scale; ball.velocity = vel; - ball.ctf_droptime = time; + ball.nb_droptime = time; ball.touch = basketball_touch; ball.think = ResetBall; ball.nextthink = min(time + g_nexball_delay_idle, ball.teamtime); @@ -302,7 +305,7 @@ void basketball_touch (void) football_touch(); return; } - if (!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_nexball_delay_collect)) { + if (!self.cnt && other.classname == "player" && (other.playerid != self.nb_dropperid || time > self.nb_droptime + autocvar_g_nexball_delay_collect)) { if (other.health <= 0) return; LogNB("caught", other); diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 002cce3308..16a7193c13 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -177,10 +177,9 @@ float autocvar_g_balance_crylink_secondary_refire; float autocvar_g_balance_crylink_secondary_shots; float autocvar_g_balance_crylink_secondary_speed; float autocvar_g_balance_crylink_secondary_spread; +float autocvar_g_balance_crylink_secondary_spreadtype; float autocvar_g_balance_crylink_reload_ammo; float autocvar_g_balance_crylink_reload_time; -float autocvar_g_balance_ctf_damageforcescale; -float autocvar_g_balance_ctf_delay_collect; float autocvar_g_balance_curse_empathy_minhealth; float autocvar_g_balance_curse_empathy_takedamage; float autocvar_g_balance_curse_slow_atkrate; @@ -686,8 +685,8 @@ float autocvar_g_balance_shotgun_secondary_refire; float autocvar_g_balance_shotgun_reload_ammo; float autocvar_g_balance_shotgun_reload_time; float autocvar_g_balance_teams; -float autocvar_g_balance_teams_force; float autocvar_g_balance_teams_prevent_imbalance; +float autocvar_g_balance_teams_scorefactor; float autocvar_g_balance_tuba_animtime; float autocvar_g_balance_tuba_attenuation; float autocvar_g_balance_tuba_damage; @@ -762,23 +761,76 @@ float autocvar_g_chat_flood_spl_team; float autocvar_g_chat_flood_spl_tell; float autocvar_g_chat_nospectators; float autocvar_g_chat_teamcolors; +float autocvar_g_ctf_allow_vehicle_carry; +float autocvar_g_ctf_allow_vehicle_touch; +float autocvar_g_ctf_throw; +float autocvar_g_ctf_throw_angle_max; +float autocvar_g_ctf_throw_angle_min; +float autocvar_g_ctf_throw_punish_count; +float autocvar_g_ctf_throw_punish_delay; +float autocvar_g_ctf_throw_punish_time; +float autocvar_g_ctf_throw_strengthmultiplier; +float autocvar_g_ctf_throw_velocity_forward; +float autocvar_g_ctf_throw_velocity_up; +float autocvar_g_ctf_drop_velocity_up; +float autocvar_g_ctf_drop_velocity_side; +float autocvar_g_ctf_portalteleport; +float autocvar_g_ctf_pass; +float autocvar_g_ctf_pass_arc; +float autocvar_g_ctf_pass_arc_max; +float autocvar_g_ctf_pass_directional_max; +float autocvar_g_ctf_pass_directional_min; +float autocvar_g_ctf_pass_radius; +float autocvar_g_ctf_pass_wait; +float autocvar_g_ctf_pass_request; +float autocvar_g_ctf_pass_turnrate; +float autocvar_g_ctf_pass_timelimit; +float autocvar_g_ctf_pass_velocity; float autocvar_g_ctf_captimerecord_always; float autocvar_g_ctf_dynamiclights; string autocvar_g_ctf_flag_blue_model; float autocvar_g_ctf_flag_blue_skin; -float autocvar_g_ctf_flag_capture_effects; +float autocvar_g_ctf_flag_collect_delay; +float autocvar_g_ctf_flag_damageforcescale; +float autocvar_g_ctf_flag_dropped_waypoint; +float autocvar_g_ctf_flag_dropped_floatinwater; float autocvar_g_ctf_flag_glowtrails; -float autocvar_g_ctf_flag_pickup_effects; +float autocvar_g_ctf_flag_health; +float autocvar_g_ctf_flag_pickup_verbosename; string autocvar_g_ctf_flag_red_model; float autocvar_g_ctf_flag_red_skin; -float autocvar_g_ctf_flag_returntime; -float autocvar_g_ctf_flagcarrier_selfdamage; -float autocvar_g_ctf_flagcarrier_selfforce; +float autocvar_g_ctf_flag_return_time; +float autocvar_g_ctf_flag_return_when_unreachable; +float autocvar_g_ctf_flag_return_damage; +float autocvar_g_ctf_flag_return_dropped; +float autocvar_g_ctf_flagcarrier_auto_helpme_damage; +float autocvar_g_ctf_flagcarrier_auto_helpme_time; +float autocvar_g_ctf_flagcarrier_selfdamagefactor; +float autocvar_g_ctf_flagcarrier_selfforcefactor; +float autocvar_g_ctf_flagcarrier_damagefactor; +float autocvar_g_ctf_flagcarrier_forcefactor; +//float autocvar_g_ctf_flagcarrier_waypointforenemy_spotting; float autocvar_g_ctf_fullbrightflags; float autocvar_g_ctf_ignore_frags; +float autocvar_g_ctf_score_capture; +float autocvar_g_ctf_score_capture_assist; +float autocvar_g_ctf_score_kill; +float autocvar_g_ctf_score_penalty_drop; +//float autocvar_g_ctf_score_penalty_suicidedrop; +float autocvar_g_ctf_score_penalty_returned; +float autocvar_g_ctf_score_pickup_base; +float autocvar_g_ctf_score_pickup_dropped_early; +float autocvar_g_ctf_score_pickup_dropped_late; +float autocvar_g_ctf_score_return; float autocvar_g_ctf_shield_force; float autocvar_g_ctf_shield_max_ratio; float autocvar_g_ctf_shield_min_negscore; +float autocvar_g_ctf_stalemate; +float autocvar_g_ctf_stalemate_endcondition; +float autocvar_g_ctf_stalemate_time; +float autocvar_g_ctf_reverse; +float autocvar_g_ctf_dropped_capture_delay; +float autocvar_g_ctf_dropped_capture_radius; float autocvar_g_cts_finish_kill_delay; float autocvar_g_cts_selfdamage; float autocvar_g_debug_bot_commands; @@ -809,7 +861,6 @@ float autocvar_g_freezetag_warmup; float autocvar_g_full_getstatus_responses; float autocvar_g_fullbrightitems; float autocvar_g_fullbrightplayers; -string autocvar_g_ghost_items_color; #define autocvar_g_grappling_hook cvar("g_grappling_hook") float autocvar_g_grappling_hook_tarzan; float autocvar_g_hitplots; @@ -928,6 +979,7 @@ float autocvar_g_playerclip_collisions; string autocvar_g_playerstats_uri; float autocvar_g_powerups; float autocvar_g_projectiles_damage; +float autocvar_g_projectiles_keep_owner; float autocvar_g_projectiles_newton_style; float autocvar_g_projectiles_newton_style_2_maxfactor; float autocvar_g_projectiles_newton_style_2_minfactor; @@ -978,8 +1030,15 @@ float autocvar_g_telefrags; float autocvar_g_telefrags_avoid; float autocvar_g_telefrags_teamplay; float autocvar_g_teleport_maxspeed; +float autocvar_g_throughfloor_debug; float autocvar_g_throughfloor_damage; float autocvar_g_throughfloor_force; +float autocvar_g_throughfloor_damage_max_stddev; +float autocvar_g_throughfloor_force_max_stddev; +float autocvar_g_throughfloor_min_steps_player; +float autocvar_g_throughfloor_max_steps_player; +float autocvar_g_throughfloor_min_steps_other; +float autocvar_g_throughfloor_max_steps_other; float autocvar_g_triggerimpulse_accel_multiplier; float autocvar_g_triggerimpulse_accel_power; float autocvar_g_triggerimpulse_directional_multiplier; diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc index 039e21daed..3e1cae313f 100644 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@ -128,7 +128,10 @@ void bot_setnameandstuff() file = fopen(autocvar_bot_config_file, FILE_READ); if(file < 0) + { print(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n")); + readfile = ""; + } else { RandomSelection_Init(); @@ -363,6 +366,7 @@ void bot_clientdisconnect() { if (clienttype(self) != CLIENTTYPE_BOT) return; + bot_clearqueue(self); if(self.cleanname) strunzone(self.cleanname); if(self.netname_freeme) diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc index 3aefc45a75..1b9178b177 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@ -1,9 +1,6 @@ #include "havocbot.qh" -#include "role_ctf.qc" #include "role_onslaught.qc" #include "role_keyhunt.qc" -#include "role_freezetag.qc" -#include "role_keepaway.qc" #include "role_assault.qc" #include "roles.qc" @@ -94,7 +91,7 @@ void havocbot_ai() self.aistatus |= AI_STATUS_ATTACKING; self.aistatus &~= AI_STATUS_ROAMING; - if(self.weapons) + if(!WEPSET_EMPTY_E(self)) { weapon_action(self.weapon, WR_AIM); if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self)) @@ -168,7 +165,7 @@ void havocbot_ai() for(i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - if ((self.weapons & W_WeaponBit(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo")))) + if (WEPSET_CONTAINS_EW(self, i) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo")))) self.switchweapon = i; } } @@ -493,8 +490,8 @@ void havocbot_movetogoal() { if(fabs(self.velocity_z)<50) { - entity head, newgoal; - float distance, bestdistance; + entity head, newgoal = world; + float distance, bestdistance = 0; for (head = findchain(classname, "waypoint"); head; head = head.chain) { @@ -908,7 +905,7 @@ void havocbot_chooseenemy() self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - for(;;) + for(i = 0; ; ++i) { while (head) { @@ -930,7 +927,7 @@ void havocbot_chooseenemy() // I want to do a second scan if no enemy was found or I don't have weapons // TODO: Perform the scan when using the rifle (requires changes on the rifle code) - if(best || self.weapons) // || self.weapon == WEP_RIFLE + if(best || !WEPSET_EMPTY_E(self)) // || self.weapon == WEP_RIFLE break; if(i) break; @@ -939,7 +936,6 @@ void havocbot_chooseenemy() self.dphitcontentsmask |= DPCONTENTS_OPAQUE; head = head2; - ++i; } // Restore hit flags @@ -960,7 +956,7 @@ float havocbot_chooseweapon_checkreload(float new_weapon) // if this weapon is scheduled for reloading, don't switch to it during combat if (self.weapon_load[new_weapon] < 0) { - float i, other_weapon_available; + float i, other_weapon_available = FALSE; for(i = WEP_FIRST; i <= WEP_LAST; ++i) { // if we are out of ammo for all other weapons, it's an emergency to switch to anything else @@ -979,7 +975,7 @@ void havocbot_chooseweapon() float i; // ;) - if(g_weaponarena == WEPBIT_TUBA) + if(WEPSET_EQ_AW(g_weaponarena_weapons, WEP_TUBA)) { self.switchweapon = WEP_TUBA; return; diff --git a/qcsrc/server/bot/havocbot/havocbot.qh b/qcsrc/server/bot/havocbot/havocbot.qh index be28962912..de94691822 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qh +++ b/qcsrc/server/bot/havocbot/havocbot.qh @@ -19,6 +19,7 @@ .float havocbot_personal_waypoint_failcounter; .float havocbot_chooseenemy_finished; .float havocbot_stickenemy; +.float havocbot_role_timeout; .entity ignoregoal; .entity bot_lastseengoal; @@ -47,6 +48,10 @@ float havocbot_moveto_refresh_route(); vector havocbot_dodge(); .void() havocbot_role; +.void() havocbot_previous_role; + +void(float ratingscale, vector org, float sradius) havocbot_goalrating_items; +void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; /* * Imports diff --git a/qcsrc/server/bot/havocbot/role_ctf.qc b/qcsrc/server/bot/havocbot/role_ctf.qc deleted file mode 100644 index 74c611ea18..0000000000 --- a/qcsrc/server/bot/havocbot/role_ctf.qc +++ /dev/null @@ -1,684 +0,0 @@ -#define HAVOCBOT_CTF_ROLE_NONE 0 -#define HAVOCBOT_CTF_ROLE_DEFENSE 2 -#define HAVOCBOT_CTF_ROLE_MIDDLE 4 -#define HAVOCBOT_CTF_ROLE_OFFENSE 8 -#define HAVOCBOT_CTF_ROLE_CARRIER 16 -#define HAVOCBOT_CTF_ROLE_RETRIEVER 32 -#define HAVOCBOT_CTF_ROLE_ESCORT 64 - -.void() havocbot_role; -.void() havocbot_previous_role; - -void() havocbot_role_ctf_middle; -void() havocbot_role_ctf_defense; -void() havocbot_role_ctf_offense; -void() havocbot_role_ctf_carrier; -void() havocbot_role_ctf_retriever; -void() havocbot_role_ctf_escort; - -void(entity bot) havocbot_ctf_reset_role; -void(float ratingscale, vector org, float sradius) havocbot_goalrating_items; -void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; - -.float havocbot_cantfindflag; -.float havocbot_role_timeout; -.entity ctf_worldflagnext; -.entity basewaypoint; - -entity ctf_worldflaglist; -vector havocbot_ctf_middlepoint; -float havocbot_ctf_middlepoint_radius; - -entity havocbot_ctf_find_flag(entity bot) -{ - entity f; - f = ctf_worldflaglist; - while (f) - { - if (bot.team == f.team) - return f; - f = f.ctf_worldflagnext; - } - return world; -} - -entity havocbot_ctf_find_enemy_flag(entity bot) -{ - entity f; - f = ctf_worldflaglist; - while (f) - { - if (bot.team != f.team) - return f; - f = f.ctf_worldflagnext; - } - return world; -} - -float havocbot_ctf_teamcount(entity bot, vector org, float radius) -{ - if not(teamplay) - return 0; - - float c; - 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) 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; - float distance, bestdistance; - distance = 10000; - FOR_EACH_PLAYER(head) - { - if(head.deadflag!=DEAD_NO) - continue; - - distance = vlen(org - head.origin); - if(distance1000) - 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 p1, p2; - - f = ctf_worldflaglist; - while (f) - { - if(p1) - p2 = f.origin; - else - p1 = f.origin; - - f = f.ctf_worldflagnext; - } - havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5); - havocbot_ctf_middlepoint_radius = vlen(p2-p1) * 0.5; -} - -void havocbot_ctf_reset_role(entity bot) -{ - float cdefense, cmiddle, coffense; - entity mf, ef, head; - float c; - - if(bot.deadflag != DEAD_NO) - return; - - if(vlen(havocbot_ctf_middlepoint)==0) - havocbot_calculate_middlepoint(); - - // Check ctf flags - if (bot.flagcarried) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - mf = havocbot_ctf_find_flag(bot); - ef = havocbot_ctf_find_enemy_flag(bot); - - // Retrieve stolen flag - if(mf.cnt!=FLAG_BASE) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - // If enemy flag is taken go to the middle to intercept pursuers - if(ef.cnt!=FLAG_BASE) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); - return; - } - - // if there is only me on the team switch to offense - c = 0; - FOR_EACH_PLAYER(head) - if(head.team==bot.team) - ++c; - - if(c==1) - { - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); - return; - } - - // Evaluate best position to take - // Count mates on middle position - cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5); - - // Count mates on defense position - cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5); - - // Count mates on offense position - coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius); - - if(cdefense<=coffense) - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE); - else if(coffense<=cmiddle) - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE); - else - havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE); -} - -void havocbot_chooserole_ctf() -{ - havocbot_ctf_reset_role(self); -} diff --git a/qcsrc/server/bot/havocbot/role_freezetag.qc b/qcsrc/server/bot/havocbot/role_freezetag.qc deleted file mode 100644 index 4e5669eb2e..0000000000 --- a/qcsrc/server/bot/havocbot/role_freezetag.qc +++ /dev/null @@ -1,109 +0,0 @@ -void() havocbot_role_ft_freeing; -void() havocbot_role_ft_offense; - -void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradius) -{ - entity head; - float distance; - - FOR_EACH_PLAYER(head) - { - if ((head != self) && (head.team == self.team)) - { - if (head.freezetag_frozen) - { - distance = vlen(head.origin - org); - if (distance > sradius) - continue; - navigation_routerating(head, ratingscale, 2000); - } - else - { - // If teamate is not frozen still seek them out as fight better - // in a group. - navigation_routerating(head, ratingscale/3, 2000); - } - } - } -} - -void havocbot_role_ft_offense() -{ - entity head; - float unfrozen; - - if(self.deadflag != DEAD_NO) - return; - - if (!self.havocbot_role_timeout) - self.havocbot_role_timeout = time + random() * 10 + 20; - - // Count how many players on team are unfrozen. - unfrozen = 0; - FOR_EACH_PLAYER(head) - { - if ((head.team == self.team) && (!head.freezetag_frozen)) - unfrozen++; - } - - // If only one left on team or if role has timed out then start trying to free players. - if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout)) - { - dprint("changing role to freeing\n"); - self.havocbot_role = havocbot_role_ft_freeing; - self.havocbot_role_timeout = 0; - return; - } - - if (time > self.bot_strategytime) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_items(10000, self.origin, 10000); - havocbot_goalrating_enemyplayers(20000, self.origin, 10000); - havocbot_goalrating_freeplayers(9000, self.origin, 10000); - //havocbot_goalrating_waypoints(1, self.origin, 1000); - navigation_goalrating_end(); - } -} - -void havocbot_role_ft_freeing() -{ - if(self.deadflag != DEAD_NO) - return; - - if (!self.havocbot_role_timeout) - self.havocbot_role_timeout = time + random() * 10 + 20; - - if (time > self.havocbot_role_timeout) - { - dprint("changing role to offense\n"); - self.havocbot_role = havocbot_role_ft_offense; - self.havocbot_role_timeout = 0; - return; - } - - if (time > self.bot_strategytime) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_items(8000, self.origin, 10000); - havocbot_goalrating_enemyplayers(10000, self.origin, 10000); - havocbot_goalrating_freeplayers(20000, self.origin, 10000); - //havocbot_goalrating_waypoints(1, self.origin, 1000); - navigation_goalrating_end(); - } -} - -void havocbot_chooserole_ft() -{ - if(self.deadflag != DEAD_NO) - return; - - if (random() < 0.5) - self.havocbot_role = havocbot_role_ft_freeing; - else - self.havocbot_role = havocbot_role_ft_offense; -} diff --git a/qcsrc/server/bot/havocbot/role_keepaway.qc b/qcsrc/server/bot/havocbot/role_keepaway.qc deleted file mode 100644 index ede6208a16..0000000000 --- a/qcsrc/server/bot/havocbot/role_keepaway.qc +++ /dev/null @@ -1,82 +0,0 @@ -void() havocbot_role_ka_carrier; -void() havocbot_role_ka_collector; -void() havocbot_chooserole_ka; - -entity ka_ball; - -// Keepaway -// If you don't have the ball, get it; if you do, kill people. - -void havocbot_goalrating_ball(float ratingscale, vector org) -{ - float t; - entity ball_owner; - ball_owner = ka_ball.owner; - - if (ball_owner == self) - return; - - // If ball is carried by player then hunt them down. - if (ball_owner) - { - t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); - navigation_routerating(ball_owner, t * ratingscale, 2000); - } - - // Ball has been dropped so collect. - navigation_routerating(ka_ball, ratingscale, 2000); -} - -void havocbot_role_ka_carrier() -{ - if (self.deadflag != DEAD_NO) - return; - - if (time > self.bot_strategytime) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_items(10000, self.origin, 10000); - havocbot_goalrating_enemyplayers(20000, self.origin, 10000); - //havocbot_goalrating_waypoints(1, self.origin, 1000); - navigation_goalrating_end(); - } - - if (!self.ballcarried) - { - self.havocbot_role = havocbot_role_ka_collector; - self.bot_strategytime = 0; - } -} - -void havocbot_role_ka_collector() -{ - if (self.deadflag != DEAD_NO) - return; - - if (time > self.bot_strategytime) - { - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - - navigation_goalrating_start(); - havocbot_goalrating_items(10000, self.origin, 10000); - havocbot_goalrating_enemyplayers(1000, self.origin, 10000); - havocbot_goalrating_ball(20000, self.origin); - navigation_goalrating_end(); - } - - if (self.ballcarried) - { - self.havocbot_role = havocbot_role_ka_carrier; - self.bot_strategytime = 0; - } -} - -void havocbot_chooserole_ka() -{ - if (self.ballcarried) - self.havocbot_role = havocbot_role_ka_carrier; - else - self.havocbot_role = havocbot_role_ka_collector; -} diff --git a/qcsrc/server/bot/havocbot/role_onslaught.qc b/qcsrc/server/bot/havocbot/role_onslaught.qc index 3ae7379be7..b17697d2f8 100644 --- a/qcsrc/server/bot/havocbot/role_onslaught.qc +++ b/qcsrc/server/bot/havocbot/role_onslaught.qc @@ -28,17 +28,18 @@ void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplay void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius) { entity head; - float t, i, c, needarmor, needweapons; + float t, i, c, needarmor = FALSE, needweapons = FALSE; // Needs armor/health? if(self.health<100) needarmor = TRUE; // Needs weapons? + c = 0; for(i = WEP_FIRST; i <= WEP_LAST ; ++i) { // Find weapon - if(power2of(i-1) & self.weapons) + if(WEPSET_CONTAINS_EW(self, i)) if(++c>=4) break; } @@ -58,7 +59,7 @@ void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float s { // gather health and armor only if (head.solid) - if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) + if ( ((head.health || head.armorvalue) && needarmor) || (!WEPSET_EMPTY_E(head) && needweapons ) ) if (vlen(head.origin - org) < sradius) { t = head.bot_pickupevalfunc(self, head); @@ -98,7 +99,7 @@ void havocbot_role_ons_setrole(entity bot, float role) float havocbot_ons_teamcount(entity bot, float role) { - float c; + float c = 0; entity head; FOR_EACH_PLAYER(head) @@ -152,6 +153,7 @@ void havocbot_goalrating_ons_controlpoints_attack(float ratingscale) // We'll consider only the best case bestvalue = 99999999999; + cp = world; for (; cp1; cp1 = cp1.chain) { if not(cp1.wpconsidered) diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc index 91eb2ec1a5..ad3bb23674 100644 --- a/qcsrc/server/bot/havocbot/roles.qc +++ b/qcsrc/server/bot/havocbot/roles.qc @@ -85,8 +85,8 @@ void havocbot_goalrating_items(float ratingscale, vector org, float sradius) if( head.armorvalue && player.armorvalue > self.armorvalue) continue; - if( head.weapons ) - if( (player.weapons & head.weapons) != head.weapons) + if( !WEPSET_EMPTY_E(head) ) + if( !WEPSET_CONTAINS_ALL_EE(player, head) ) continue; if (head.ammo_shells && player.ammo_shells > self.ammo_shells) @@ -293,8 +293,8 @@ void havocbot_chooserole() { dprint("choosing a role...\n"); self.bot_strategytime = 0; - if (g_ctf) - havocbot_chooserole_ctf(); + if (MUTATOR_CALLHOOK(HavocBot_ChooseRule)) + return; else if (g_domination) havocbot_chooserole_dom(); else if (g_keyhunt) @@ -303,10 +303,6 @@ void havocbot_chooserole() havocbot_chooserole_race(); else if (g_onslaught) havocbot_chooserole_ons(); - else if (g_keepaway) - havocbot_chooserole_ka(); - else if (g_freezetag) - havocbot_chooserole_ft(); else if (g_assault) havocbot_chooserole_ast(); else // assume anything else is deathmatch diff --git a/qcsrc/server/bot/navigation.qc b/qcsrc/server/bot/navigation.qc index bf963bc7cd..a5c60e3935 100644 --- a/qcsrc/server/bot/navigation.qc +++ b/qcsrc/server/bot/navigation.qc @@ -27,6 +27,7 @@ float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float dir = normalize(move); stepdist = 32; ignorehazards = FALSE; + swimming = FALSE; // Analyze starting point traceline(start, start, MOVE_NORMAL, e); @@ -1218,6 +1219,7 @@ void debuggoalstack() else if(self.goalcounter==29)goal=self.goalstack29; else if(self.goalcounter==30)goal=self.goalstack30; else if(self.goalcounter==31)goal=self.goalstack31; + else goal=world; if(goal==world) { diff --git a/qcsrc/server/bot/scripting.qc b/qcsrc/server/bot/scripting.qc index 63a6cb336d..ace11c6d69 100644 --- a/qcsrc/server/bot/scripting.qc +++ b/qcsrc/server/bot/scripting.qc @@ -6,7 +6,7 @@ void bot_clearqueue(entity bot) { if(!bot.bot_cmdqueuebuf_allocated) - error("clearqueue but no queue allocated"); + return; buf_del(bot.bot_cmdqueuebuf); bot.bot_cmdqueuebuf_allocated = FALSE; dprint("bot ", bot.netname, " queue cleared\n"); @@ -18,6 +18,8 @@ void bot_queuecommand(entity bot, string cmdstring) { bot.bot_cmdqueuebuf = buf_create(); bot.bot_cmdqueuebuf_allocated = TRUE; + bot.bot_cmdqueuebuf_start = 0; + bot.bot_cmdqueuebuf_end = 0; } bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring); @@ -29,18 +31,24 @@ void bot_queuecommand(entity bot, string cmdstring) string parm; string cmdstr; - sp = strstrofs(cmdstr, " ", 0); - if(sp < 0) + sp = strstrofs(cmdstring, " ", 0); + if(sp >= 0) { - parm = ""; - } - else - { - parm = substring(cmdstr, sp + 1, -1); - cmdstr = substring(cmdstr, 0, sp); + parm = substring(cmdstring, sp + 1, -1); + cmdstr = substring(cmdstring, 0, sp); + if(cmdstr == "sound") + { + // find the LAST word + for(;;) + { + sp = strstrofs(parm, " ", 0); + if(sp < 0) + break; + parm = substring(parm, sp + 1, -1); + } + precache_sound(parm); + } } - if(cmdstr == "sound") - precache_sound(cmdstr); } bot.bot_cmdqueuebuf_end += 1; @@ -292,7 +300,7 @@ entity find_bot_by_name(string name) entity find_bot_by_number(float number) { entity bot; - float c; + float c = 0; if(!number) return world; @@ -1089,8 +1097,24 @@ float bot_cmd_sound() string f; f = bot_cmd.bot_cmd_parm_string; + float n = tokenizebyseparator(f, " "); + + string sample = f; + float chan = CH_WEAPON_B; + float vol = VOL_BASE; + float atten = ATTN_MIN; + + if(n >= 1) + sample = argv(n - 1); + if(n >= 2) + chan = stof(argv(0)); + if(n >= 3) + vol = stof(argv(1)); + if(n >= 4) + atten = stof(argv(2)); + precache_sound(f); - sound(self, CH_WEAPON_B, f, VOL_BASE, ATTN_MIN); + sound(self, chan, sample, vol, atten); return CMD_STATUS_FINISHED; } @@ -1194,8 +1218,8 @@ void bot_resetqueues() FOR_EACH_CLIENT(cl) if(cl.isbot) { - if(cl.bot_cmdqueuebuf_allocated) - bot_clearqueue(cl); + cl.bot_cmd_execution_index = 0; + bot_clearqueue(cl); // also, cancel all barriers cl.bot_barrier = 0; for(i = 0; i < cl.bot_places_count; ++i) diff --git a/qcsrc/server/campaign.qc b/qcsrc/server/campaign.qc index 7c2509d21a..b69e0fbe09 100644 --- a/qcsrc/server/campaign.qc +++ b/qcsrc/server/campaign.qc @@ -183,7 +183,7 @@ void CampaignPreIntermission() float lost; string savevar; - won = 0; + won = lost = 0; head = findchain(classname, "player"); while(head) diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 5ba860940a..746a22d894 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -48,7 +48,7 @@ float CheatsAllowed(float i, float argc, float fr) // the cheat gets passed as a // dead people cannot cheat if(self.deadflag != DEAD_NO) return 0; - if(self.classname != "player") + if(gamestart_sv_cheats < 2 && self.classname != "player") return 0; // sv_clones @@ -126,7 +126,6 @@ float CheatImpulse(float i) switch(i) { entity e, e2; - vector org; case CHIMPULSE_SPEEDRUN_INIT: // deploy personal waypoint // shared with regular waypoint init, so this is not a cheat by itself @@ -145,7 +144,7 @@ float CheatImpulse(float i) self.personal.ammo_fuel = self.ammo_fuel; self.personal.health = self.health; self.personal.armorvalue = self.armorvalue; - self.personal.weapons = self.weapons; + WEPSET_COPY_EE(self.personal, self); self.personal.items = self.items; self.personal.pauserotarmor_finished = self.pauserotarmor_finished; self.personal.pauserothealth_finished = self.pauserothealth_finished; @@ -191,47 +190,26 @@ float CheatImpulse(float i) self.oldvelocity = self.velocity = self.personal.velocity; self.angles = self.personal.v_angle; self.fixangle = TRUE; - if(self.flagcarried) - { - bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n"); - ReturnFlag(self.flagcarried); - } - } - if(g_ctf) - { - self.ammo_rockets = 999; - self.ammo_nails = 999; - self.ammo_cells = 999; - self.ammo_shells = 999; - self.ammo_fuel = 999; - self.health = start_health; - self.armorvalue = start_armorvalue; - self.weapons |= 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; - self.weapons = self.personal.weapons; - self.items = self.personal.items; - self.pauserotarmor_finished = time + self.personal.pauserotarmor_finished - self.personal.teleport_time; - self.pauserothealth_finished = time + self.personal.pauserothealth_finished - self.personal.teleport_time; - self.pauserotfuel_finished = time + self.personal.pauserotfuel_finished - self.personal.teleport_time; - self.pauseregen_finished = time + self.personal.pauseregen_finished - self.personal.teleport_time; - self.strength_finished = time + self.personal.strength_finished - self.personal.teleport_time; - self.invincible_finished = time + self.personal.invincible_finished - self.personal.teleport_time; + + MUTATOR_CALLHOOK(AbortSpeedrun); } + + self.ammo_rockets = self.personal.ammo_rockets; + self.ammo_nails = self.personal.ammo_nails; + self.ammo_cells = self.personal.ammo_cells; + self.ammo_shells = self.personal.ammo_shells; + self.ammo_fuel = self.personal.ammo_fuel; + self.health = self.personal.health; + self.armorvalue = self.personal.armorvalue; + WEPSET_COPY_EE(self, self.personal); + self.items = self.personal.items; + self.pauserotarmor_finished = time + self.personal.pauserotarmor_finished - self.personal.teleport_time; + self.pauserothealth_finished = time + self.personal.pauserothealth_finished - self.personal.teleport_time; + self.pauserotfuel_finished = time + self.personal.pauserotfuel_finished - self.personal.teleport_time; + self.pauseregen_finished = time + self.personal.pauseregen_finished - self.personal.teleport_time; + self.strength_finished = time + self.personal.strength_finished - self.personal.teleport_time; + self.invincible_finished = time + self.personal.invincible_finished - self.personal.teleport_time; + DID_CHEAT(); break; } @@ -258,7 +236,7 @@ float CheatImpulse(float i) break; } } - if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats >= 2) ? 100000 : 100), 1024, 256)) + if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats < 2) ? 100 : 100000), 1024, 256)) { sprint(self, "Emergency teleport used random location\n"); self.angles_x = -self.angles_x; @@ -271,32 +249,24 @@ float CheatImpulse(float i) break; case CHIMPULSE_R00T: IS_CHEAT(i, 0, 0); + RandomSelection_Init(); FOR_EACH_PLAYER(e) - { - get_model_parameters(e.playermodel, e.skin); - if(get_model_parameters_sex == "Female") - { - makevectors(e.angles); - traceline(e.origin, e.origin + v_right * 256, MOVE_NORMAL, e); - } - else - { - org_x = random(); - org_y = random(); - org_z = 0; - org = normalize(org); - traceline(e.origin, e.origin + org * 256, MOVE_NORMAL, e); // random direction - } + if(e.deadflag == DEAD_NO) + if(IsDifferentTeam(e, self)) + RandomSelection_Add(e, 0, string_null, 1, 1); + if(RandomSelection_chosen_ent) + e = RandomSelection_chosen_ent; + else + e = self; - org = findbetterlocation(trace_endpos, 12); + pointparticles(particleeffectnum("rocket_explode"), e.origin, '0 0 0', 1); + sound(e, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + + e2 = spawn(); + setorigin(e2, e.origin); + RadiusDamage(e2, self, 1000, 0, 128, world, 500, DEATH_CHEAT, e); + remove(e2); - e2 = spawn(); - setorigin(e2, org); - pointparticles(particleeffectnum("rocket_explode"), org, '0 0 0', 1); - sound(e2, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - RadiusDamage(e2, e, 1000, 0, 128, e, 500, DEATH_CHEAT, world); - remove(e2); - } print("404 Sportsmanship not found.\n"); DID_CHEAT(); break; @@ -703,6 +673,46 @@ float CheatCommand(float argc) if(GiveItems(self, 1, argc)) DID_CHEAT(); break; + case "usetarget": + IS_CHEAT(0, argc, 0); + e = self; + self = spawn(); + self.target = argv(1); + activator = e; + SUB_UseTargets(); + remove(self); + self = e; + DID_CHEAT(); + break; + case "killtarget": + IS_CHEAT(0, argc, 0); + e = self; + self = spawn(); + self.killtarget = argv(1); + activator = e; + SUB_UseTargets(); + remove(self); + self = e; + DID_CHEAT(); + break; + case "teleporttotarget": + IS_CHEAT(0, argc, 0); + e = self; + self = spawn(); + setorigin(self, self.origin); + self.classname = "cheattriggerteleport"; + self.target = argv(1); + teleport_findtarget(); + if(!wasfreed(self)) + { + Simple_TeleportPlayer(self, e); + remove(self); + self = e; + DID_CHEAT(); + } + else + self = e; + break; } END_CHEAT_FUNCTION(); @@ -749,6 +759,7 @@ float CheatFrame() float drag; crosshair_trace_plusvisibletriggers(self); + drag = FALSE; if(vlen(self.origin - trace_ent.origin) <= autocvar_g_grab_range) { switch(trace_ent.grab) diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 848cbe030a..101e35cae2 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -415,10 +415,7 @@ void PutObserverInServer (void) } if(self.vehicle) - vehicles_exit(VHEF_RELESE); - - if(self.flagcarried) - DropFlag(self.flagcarried, world, world); + vehicles_exit(VHEF_RELESE); WaypointSprite_PlayerDead(); @@ -448,6 +445,7 @@ void PutObserverInServer (void) self.classname = "observer"; self.iscreature = FALSE; + self.teleportable = TELEPORT_SIMPLE; self.damagedbycontents = FALSE; self.health = -666; self.takedamage = DAMAGE_NO; @@ -472,6 +470,7 @@ void PutObserverInServer (void) self.invincible_finished = 0; self.superweapons_finished = 0; self.pushltime = 0; + self.istypefrag = 0; self.think = SUB_Null; self.nextthink = 0; self.hook_time = 0; @@ -485,7 +484,7 @@ void PutObserverInServer (void) setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way self.prevorigin = self.origin; self.items = 0; - self.weapons = 0; + WEPSET_CLEAR_E(self); self.model = ""; FixPlayermodel(); setmodel(self, "null"); @@ -547,11 +546,11 @@ void FixPlayermodel() vector m1, m2; defaultmodel = ""; + defaultskin = 0; + chmdl = FALSE; if(autocvar_sv_defaultcharacter == 1) { - defaultskin = 0; - if(teamplay) { string s; @@ -640,7 +639,6 @@ PutClientInServer Called when a client spawns in the server ============= */ -//void() ctf_playerchanged; void PutClientInServer (void) { @@ -697,6 +695,7 @@ void PutClientInServer (void) self.classname = "player"; self.wasplayer = TRUE; self.iscreature = TRUE; + self.teleportable = TELEPORT_NORMAL; self.damagedbycontents = TRUE; self.movetype = MOVETYPE_WALK; self.solid = SOLID_SLIDEBOX; @@ -735,7 +734,7 @@ void PutClientInServer (void) self.ammo_fuel = warmup_start_ammo_fuel; self.health = warmup_start_health; self.armorvalue = warmup_start_armorvalue; - self.weapons = warmup_start_weapons; + WEPSET_COPY_EA(self, warmup_start_weapons); } else { @@ -746,10 +745,10 @@ void PutClientInServer (void) self.ammo_fuel = start_ammo_fuel; self.health = start_health; self.armorvalue = start_armorvalue; - self.weapons = start_weapons; + WEPSET_COPY_EA(self, start_weapons); } - if(self.weapons & WEPBIT_SUPERWEAPONS) // exception for minstagib, as minstanex is a superweapon + if(WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) // exception for minstagib, as minstanex is a superweapon self.superweapons_finished = time + autocvar_g_balance_superweapons_time; else self.superweapons_finished = 0; @@ -757,10 +756,10 @@ void PutClientInServer (void) if(g_weaponarena_random) { if(g_weaponarena_random_with_laser) - self.weapons &~= WEPBIT_LASER; - self.weapons = randombits(self.weapons, g_weaponarena_random, FALSE); + WEPSET_ANDNOT_EW(self, WEP_LASER); + W_RandomWeapons(self, g_weaponarena_random); if(g_weaponarena_random_with_laser) - self.weapons |= WEPBIT_LASER; + WEPSET_OR_EW(self, WEP_LASER); } self.items = start_items; @@ -867,9 +866,6 @@ void PutClientInServer (void) //stuffcmd(self, "chase_active 0"); //stuffcmd(self, "set viewsize $tmpviewsize \n"); - if (autocvar_g_spawnsound) - sound (self, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTN_NORM); - if(g_assault) { if(self.team == assault_attacker_team) centerprint(self, "You are attacking!"); @@ -921,12 +917,12 @@ void PutClientInServer (void) self.alivetime = time; antilag_clear(self); + + if (autocvar_g_spawnsound) + soundat(world, self.origin, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTN_NORM); } else if(self.classname == "observer") { PutObserverInServer (); } - - //if(g_ctf) - // ctf_playerchanged(); } .float ebouncefactor, ebouncestop; // electro's values @@ -1069,8 +1065,7 @@ void ClientKill_Now_TeamChange() { if(self.killindicator_teamchange == -1) { - self.team = -1; - JoinBestTeam( self, FALSE, FALSE ); + JoinBestTeam( self, FALSE, TRUE ); } else if(self.killindicator_teamchange == -2) { @@ -1118,7 +1113,7 @@ void KillIndicator_Think() return; } - if (self.owner.alpha < 0) + if (self.owner.alpha < 0 && !self.owner.vehicle) { self.owner.killindicator = world; remove(self); @@ -1340,7 +1335,6 @@ ClientConnect Called when a client connects to the server ============= */ -//void ctf_clientconnect(); string ColoredTeamName(float t); void DecodeLevelParms (void); //void dom_player_join_team(entity pl); @@ -1355,7 +1349,7 @@ void ClientConnect (void) return; } - if(Ban_MaybeEnforceBan(self)) + if(Ban_MaybeEnforceBanOnce(self)) return; DecodeLevelParms(); @@ -1439,7 +1433,7 @@ void ClientConnect (void) } else { if(teamplay) { - if(autocvar_g_balance_teams || autocvar_g_balance_teams_force) + if(autocvar_g_balance_teams) { self.classname = "player"; campaign_bots_may_start = 1; @@ -1512,10 +1506,6 @@ void ClientConnect (void) if(g_arena) Spawnqueue_Insert(self); } - /*else if(g_ctf) - { - ctf_clientconnect(); - }*/ attach_entcs(); @@ -1532,7 +1522,7 @@ void ClientConnect (void) if(clienttype(self) == CLIENTTYPE_REAL) { - if(autocvar_g_bugrigs || g_weaponarena == WEPBIT_TUBA) + if(autocvar_g_bugrigs || WEPSET_EQ_AW(g_weaponarena_weapons, WEP_TUBA)) stuffcmd(self, "cl_cmd settemp chase_active 1\n"); } @@ -1564,7 +1554,6 @@ void ClientConnect (void) rr = CTS_RECORD; else rr = RACE_RECORD; - t = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "time"))); msg_entity = self; race_send_recordtime(MSG_ONE); @@ -1598,6 +1587,8 @@ void ClientConnect (void) return; sv_notice_join(); + + MUTATOR_CALLHOOK(ClientConnect); } /* ============= @@ -1652,8 +1643,6 @@ void ClientDisconnect (void) Portal_ClearAll(self); RemoveGrapplingHook(self); - if(self.flagcarried) - DropFlag(self.flagcarried, world, world); // Here, everything has been done that requires this player to be a client. @@ -1895,9 +1884,7 @@ void player_powerups (void) } if (self.items & IT_SUPERWEAPON) { - //if(W_WeaponBit(self.weapon) & WEPBIT_SUPERWEAPONS) - // self.effects = self.effects | EF_RED; - if (!(self.weapons & WEPBIT_SUPERWEAPONS)) + if (!WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) { self.superweapons_finished = 0; self.items = self.items - (self.items & IT_SUPERWEAPON); @@ -1913,12 +1900,12 @@ void player_powerups (void) if (time > self.superweapons_finished) { self.items = self.items - (self.items & IT_SUPERWEAPON); - self.weapons &~= WEPBIT_SUPERWEAPONS; + WEPSET_ANDNOT_EA(self, WEPBIT_SUPERWEAPONS); sprint(self, "^3Superweapons have broken down\n"); } } } - else if(self.weapons & WEPBIT_SUPERWEAPONS) + else if(WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) { if (time < self.superweapons_finished || (self.items & IT_UNLIMITED_SUPERWEAPONS)) { @@ -1928,7 +1915,7 @@ void player_powerups (void) else { self.superweapons_finished = 0; - self.weapons &~= WEPBIT_SUPERWEAPONS; // just in case + WEPSET_ANDNOT_EA(self, WEPBIT_SUPERWEAPONS); } } else @@ -2125,6 +2112,15 @@ void GetPressedKeys(void) { self.pressedkeys |= KEY_CROUCH; else self.pressedkeys &~= KEY_CROUCH; + + if (self.BUTTON_ATCK) + self.pressedkeys |= KEY_ATCK; + else + self.pressedkeys &~= KEY_ATCK; + if (self.BUTTON_ATCK2) + self.pressedkeys |= KEY_ATCK2; + else + self.pressedkeys &~= KEY_ATCK2; } /* @@ -2155,7 +2151,7 @@ void SpectateCopy(entity spectatee) { self.strength_finished = spectatee.strength_finished; self.invincible_finished = spectatee.invincible_finished; self.pressedkeys = spectatee.pressedkeys; - self.weapons = spectatee.weapons; + WEPSET_COPY_EE(self, spectatee); self.switchweapon = spectatee.switchweapon; self.switchingweapon = spectatee.switchingweapon; self.weapon = spectatee.weapon; @@ -2165,29 +2161,25 @@ void SpectateCopy(entity spectatee) { self.minelayer_mines = spectatee.minelayer_mines; self.punchangle = spectatee.punchangle; self.view_ofs = spectatee.view_ofs; - self.v_angle = spectatee.v_angle; self.velocity = spectatee.velocity; self.dmg_take = spectatee.dmg_take; self.dmg_save = spectatee.dmg_save; self.dmg_inflictor = spectatee.dmg_inflictor; + self.v_angle = spectatee.v_angle; self.angles = spectatee.v_angle; + self.stat_respawn_time = spectatee.stat_respawn_time; if(!self.BUTTON_USE) self.fixangle = TRUE; setorigin(self, spectatee.origin); setsize(self, spectatee.mins, spectatee.maxs); SetZoomState(spectatee.zoomstate); - - anticheat_spectatecopy(spectatee); - - //self.vehicle = spectatee.vehicle; - + + anticheat_spectatecopy(spectatee); self.hud = spectatee.hud; if(spectatee.vehicle) { - setorigin(self, spectatee.origin); - self.velocity = spectatee.vehicle.velocity; - self.v_angle += spectatee.vehicle.angles; - //self.v_angle_x *= -1; + self.fixangle = FALSE; + //self.velocity = spectatee.vehicle.velocity; self.vehicle_health = spectatee.vehicle_health; self.vehicle_shield = spectatee.vehicle_shield; self.vehicle_energy = spectatee.vehicle_energy; @@ -2195,11 +2187,18 @@ void SpectateCopy(entity spectatee) { self.vehicle_ammo2 = spectatee.vehicle_ammo2; self.vehicle_reload1 = spectatee.vehicle_reload1; self.vehicle_reload2 = spectatee.vehicle_reload2; - + msg_entity = self; - WriteByte (MSG_ONE, SVC_SETVIEWPORT); - WriteEntity(MSG_ONE, spectatee); - //self.tur_head = spectatee.vehicle.vehicle_viewport; + + WriteByte (MSG_ONE, SVC_SETVIEWANGLES); + WriteAngle(MSG_ONE, spectatee.v_angle_x); + WriteAngle(MSG_ONE, spectatee.v_angle_y); + WriteAngle(MSG_ONE, spectatee.v_angle_z); + + //WriteByte (MSG_ONE, SVC_SETVIEW); + // WriteEntity(MSG_ONE, self); + //makevectors(spectatee.v_angle); + //setorigin(self, spectatee.origin - v_forward * 400 + v_up * 300);*/ } } @@ -2242,8 +2241,13 @@ entity CA_SpectateNext(entity start) { return other; } -float SpectateNext() { - other = find(self.enemy, classname, "player"); +float SpectateNext(entity _prefer) { + + if(_prefer) + other = _prefer; + else + other = find(self.enemy, classname, "player"); + if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) { // CA and ca players when spectating enemies is forbidden other = CA_SpectateNext(other); @@ -2257,17 +2261,19 @@ float SpectateNext() { self.enemy = other; if(self.enemy.classname == "player") { - if(self.enemy.vehicle) + /*if(self.enemy.vehicle) { + msg_entity = self; - WriteByte(MSG_ONE, SVC_SETVIEWPORT); + WriteByte(MSG_ONE, SVC_SETVIEW); WriteEntity(MSG_ONE, self.enemy); //stuffcmd(self, "set viewsize $tmpviewsize \n"); + self.movetype = MOVETYPE_NONE; accuracy_resend(self); } else - { + {*/ msg_entity = self; WriteByte(MSG_ONE, SVC_SETVIEW); WriteEntity(MSG_ONE, self.enemy); @@ -2277,7 +2283,7 @@ float SpectateNext() { if(!SpectateUpdate()) PutObserverInServer(); - } + //} return 1; } else { return 0; @@ -2317,7 +2323,7 @@ void LeaveSpectatorMode() if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0) { self.classname = "player"; - if(autocvar_g_campaign || autocvar_g_balance_teams || autocvar_g_balance_teams_force) + if(autocvar_g_campaign || autocvar_g_balance_teams) JoinBestTeam(self, FALSE, TRUE); if(autocvar_g_campaign) @@ -2379,7 +2385,7 @@ float nJoinAllowed(entity ignore) { // TODO simplify this entity e; - float totalClients; + float totalClients = 0; FOR_EACH_CLIENT(e) if(e != ignore) totalClients += 1; @@ -2387,7 +2393,7 @@ float nJoinAllowed(entity ignore) { if (!autocvar_g_maxplayers) return maxclients - totalClients; - float currentlyPlaying; + float currentlyPlaying = 0; FOR_EACH_REALPLAYER(e) currentlyPlaying += 1; @@ -2455,7 +2461,7 @@ void ObserverThink() self.flags |= FL_SPAWNING; } else if(self.BUTTON_ATCK && !self.version_mismatch) { self.flags &~= FL_JUMPRELEASED; - if(SpectateNext() == 1) { + if(SpectateNext(world) == 1) { self.classname = "spectator"; } } else { @@ -2486,7 +2492,7 @@ void SpectatorThink() self.flags |= FL_SPAWNING; } else if(self.BUTTON_ATCK) { self.flags &~= FL_JUMPRELEASED; - if(SpectateNext() == 1) { + if(SpectateNext(world) == 1) { self.classname = "spectator"; } else { self.classname = "observer"; @@ -2518,7 +2524,6 @@ void SpectatorThink() self.flags |= FL_CLIENT | FL_NOTARGET; } -float ctf_usekey(); void PlayerUseKey() { if(self.classname != "player") @@ -2531,9 +2536,6 @@ void PlayerUseKey() } // a use key was pressed; call handlers - if(ctf_usekey()) - return; - MUTATOR_CALLHOOK(PlayerUseKey); } @@ -2547,7 +2549,6 @@ Called every frame for each client before the physics are run ============= */ .float usekeypressed; -void() ctf_setstatus; void() nexball_setstatus; .float items_added; void PlayerPreThink (void) @@ -2558,6 +2559,11 @@ void PlayerPreThink (void) self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam; self.stat_leadlimit = autocvar_leadlimit; + if(g_arena || (g_ca && !allowed_to_spawn)) + self.stat_respawn_time = 0; + else + self.stat_respawn_time = self.respawn_time; + if(frametime) { // physics frames: update anticheat stuff @@ -2730,6 +2736,11 @@ void PlayerPreThink (void) } ShowRespawnCountdown(); } + + // if respawning, invert stat_respawn_time to indicate this, the client translates it + if(self.deadflag == DEAD_RESPAWNING && self.stat_respawn_time > 0) + self.stat_respawn_time *= -1; + return; } // FIXME from now on self.deadflag is always 0 (and self.health is never < 1) @@ -2784,7 +2795,8 @@ void PlayerPreThink (void) self.prevorigin = self.origin; - if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && self.animstate_startframe != self.anim_melee_x) // prevent crouching if using melee attack + if (!self.vehicle) + if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && self.animstate_startframe != self.anim_melee_x && !self.freezetag_frozen) // prevent crouching if using melee attack { if (!self.crouch) { @@ -2845,9 +2857,6 @@ void PlayerPreThink (void) if(frametime) player_anim(); - if(g_ctf) - ctf_setstatus(); - if(g_nexball) nexball_setstatus(); diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc index 524e796af2..529567a3b4 100644 --- a/qcsrc/server/cl_impulse.qc +++ b/qcsrc/server/cl_impulse.qc @@ -48,7 +48,13 @@ void ImpulseCommands (void) if (timeout_status == TIMEOUT_ACTIVE) //don't allow any impulses while the game is paused return; - + + if(self.vehicle) + if(self.vehicle.deadflag == DEAD_NO) + if(self.vehicle.vehicles_impusle) + if(self.vehicle.vehicles_impusle(imp)) + return; + if(CheatImpulse(imp)) { } @@ -159,11 +165,14 @@ void ImpulseCommands (void) case 33: if(self.deadflag == DEAD_NO && teamplay) { - wp = WaypointSprite_Attach("helpme", TRUE, RADARICON_HELPME, '1 0.5 0'); - if(!wp) - WaypointSprite_HelpMePing(self.waypointsprite_attachedforcarrier); - else - WaypointSprite_Ping(wp); + if not(MUTATOR_CALLHOOK(HelpMePing)) + { + wp = WaypointSprite_Attach("helpme", TRUE, RADARICON_HELPME, '1 0.5 0'); + if(!wp) + WaypointSprite_HelpMePing(self.waypointsprite_attachedforcarrier); + else + WaypointSprite_Ping(wp); + } sprint(self, "HELP ME attached\n"); } break; diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc index 51bfc62c63..3e2268d897 100644 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@ -1074,7 +1074,7 @@ void SV_PlayerPhysics() PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0); } } - else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO)) + else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.freezetag_frozen) { //makevectors(self.v_angle_y * '0 1 0'); makevectors(self.v_angle); @@ -1176,7 +1176,7 @@ void SV_PlayerPhysics() else if (self.flags & FL_ONGROUND) { // we get here if we ran out of ammo - if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32)) + if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01) sprint(self, "You don't have any fuel for the ^2Jetpack\n"); // walking @@ -1259,7 +1259,7 @@ void SV_PlayerPhysics() { float wishspeed0; // we get here if we ran out of ammo - if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32)) + if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01) sprint(self, "You don't have any fuel for the ^2Jetpack\n"); if(maxspd_mod < 1) diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 5a16274238..dcf1ab7684 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -123,6 +123,7 @@ void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot) .entity pusher; .float pushltime; +.float istypefrag; .float CopyBody_nextthink; .void(void) CopyBody_think; @@ -151,6 +152,7 @@ void CopyBody(float keepvelocity) self.lip = oldself.lip; self.colormap = oldself.colormap; self.iscreature = oldself.iscreature; + self.teleportable = oldself.teleportable; self.damagedbycontents = oldself.damagedbycontents; self.angles = oldself.angles; self.avelocity = oldself.avelocity; @@ -271,7 +273,9 @@ void player_anim (void) if (!self.animstate_override) { - if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP) + if (self.freezetag_frozen) + setanim(self, self.anim_idle, TRUE, FALSE, FALSE); + else if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP) { if (self.crouch) { @@ -360,7 +364,7 @@ void SpawnThrownWeapon (vector org, float w) float j; for(j = WEP_FIRST; j <= WEP_LAST; ++j) { - if(self.weapons & W_WeaponBit(j)) + if(WEPSET_CONTAINS_EW(self, j)) if(W_IsWeaponThrowable(j)) W_ThrowNewWeapon(self, j, FALSE, org, randomvec() * 175 + '0 0 325'); } @@ -422,6 +426,7 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float self.alpha = -1; self.solid = SOLID_NOT; // restore later self.takedamage = DAMAGE_NO; // restore later + self.damagedbycontents = FALSE; } } @@ -491,6 +496,30 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht take = damage; } + if(attacker == self) + { + // 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) + { + attacker = self.pusher; + self.pushltime = max(self.pushltime, time + 0.6); + } + else + { + self.pushltime = 0; + self.istypefrag = 0; + } + frag_inflictor = inflictor; frag_attacker = attacker; frag_target = self; @@ -575,30 +604,16 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht self.dmg_take = self.dmg_take + take;//max(take - 10, 0); self.dmg_inflictor = inflictor; - if(attacker == self) - { - // don't reset pushltime for self damage as it may be an attempt to - // escape a lava pit or similar - //self.pushltime = 0; - } - else if(attacker.classname == "player") - { - self.pusher = attacker; - self.pushltime = time + autocvar_g_maxpushtime; - } - else if(time < self.pushltime) - { - attacker = self.pusher; - self.pushltime = max(self.pushltime, time + 0.6); - } - else - self.pushltime = 0; + if(g_ca && self != attacker && attacker.classname == "player") + PlayerScore_Add(attacker, SP_SCORE, (damage - excess) * autocvar_g_ca_damage2score_multiplier); float abot, vbot, awep; abot = (clienttype(attacker) == CLIENTTYPE_BOT); vbot = (clienttype(self) == CLIENTTYPE_BOT); valid_damage_for_weaponstats = 0; + awep = 0; + if(vbot || clienttype(self) == CLIENTTYPE_REAL) if(abot || clienttype(attacker) == CLIENTTYPE_REAL) if(attacker && self != attacker) @@ -693,15 +708,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht RemoveGrapplingHook(self); - if(self.flagcarried) - { - if(attacker.classname != "player") - DropFlag(self.flagcarried, self, attacker); // penalty for flag loss by suicide - else if(attacker.team == self.team) - DropFlag(self.flagcarried, attacker, attacker); // penalty for flag loss by suicide/teamkill - else - DropFlag(self.flagcarried, world, attacker); - } Portal_ClearAllLater(self); if(clienttype(self) == CLIENTTYPE_REAL) @@ -805,14 +811,12 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f // 0 = reject // -1 = fake accept { - string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr, privatemsgprefix; - float flood, privatemsgprefixlen; + string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr; + float flood; var .float flood_field; entity head; float ret; - - if(Ban_MaybeEnforceBan(source)) - return 0; + string privatemsgprefix = string_null; float privatemsgprefixlen = 0; if(!teamsay && !privatesay) if(substring(msgin, 0, 1) == " ") @@ -825,7 +829,10 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f else if(teamplay) colorstr = Team_ColorCode(source.team); else + { + colorstr = ""; teamsay = FALSE; + } if(intermission_running) teamsay = FALSE; @@ -884,6 +891,7 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f // FLOOD CONTROL flood = 0; + flood_field = floodcontrol_chat; if(floodcontrol) { float flood_spl; @@ -979,7 +987,7 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f if(source.classname != "player") { if not(intermission_running) - if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !inWarmupStage)) + if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(inWarmupStage || gameover))) teamsay = -1; // spectators } diff --git a/qcsrc/server/cl_weapons.qc b/qcsrc/server/cl_weapons.qc index 4a12ce1850..236c0923fd 100644 --- a/qcsrc/server/cl_weapons.qc +++ b/qcsrc/server/cl_weapons.qc @@ -163,11 +163,6 @@ string W_Name(float weaponid) return (get_weaponinfo(weaponid)).message; } -float W_WeaponBit(float wpn) -{ - return (get_weaponinfo(wpn)).weapons; -} - float W_AmmoItemCode(float wpn) { return (get_weaponinfo(wpn)).items & IT_AMMO; @@ -203,7 +198,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto wep.flags |= FL_TOSSED; wep.colormap = own.colormap; - if(W_WeaponBit(wpn) & WEPBIT_SUPERWEAPONS) + if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, wpn)) { if(own.items & IT_UNLIMITED_SUPERWEAPONS) { @@ -213,8 +208,9 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto { float superweapons = 1; for(i = WEP_FIRST; i <= WEP_LAST; ++i) - if(own.weapons & WEPBIT_SUPERWEAPONS & W_WeaponBit(i)) - ++superweapons; + if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, i)) + if(WEPSET_CONTAINS_EW(own, i)) + ++superweapons; if(superweapons <= 1) { wep.superweapons_finished = own.superweapons_finished; @@ -262,7 +258,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto if(wa & j) { ammofield = Item_CounterField(j); - wep.ammofield = 0; // if our weapon is loaded, give its load back to the player if(self.(weapon_load[self.weapon]) > 0) @@ -270,6 +265,8 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto own.ammofield += self.(weapon_load[self.weapon]); self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading } + + wep.ammofield = 0; } } } @@ -280,10 +277,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto if(wa & j) { ammofield = Item_CounterField(j); - thisammo = min(own.ammofield, wep.ammofield); - wep.ammofield = thisammo; - own.ammofield -= thisammo; - s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j)); // if our weapon is loaded, give its load back to the player if(self.(weapon_load[self.weapon]) > 0) @@ -291,6 +284,11 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto own.ammofield += self.(weapon_load[self.weapon]); self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading } + + thisammo = min(own.ammofield, wep.ammofield); + wep.ammofield = thisammo; + own.ammofield -= thisammo; + s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j)); } } s = substring(s, 5, -1); @@ -306,7 +304,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto float W_IsWeaponThrowable(float w) { - float wb, wa; + float wa; if (!autocvar_g_pickup_items) return 0; @@ -320,12 +318,11 @@ float W_IsWeaponThrowable(float w) return 0; if (g_nexball && w == WEP_GRENADE_LAUNCHER) return 0; - - wb = W_WeaponBit(w); - if(!wb) - return 0; + if(w == 0) + return 0; + wa = W_AmmoItemCode(w); - if(start_weapons & wb) + if(WEPSET_CONTAINS_AW(start_weapons, w)) { // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo) if(start_items & IT_UNLIMITED_WEAPON_AMMO) @@ -340,7 +337,7 @@ float W_IsWeaponThrowable(float w) // toss current weapon void W_ThrowWeapon(vector velo, vector delta, float doreduce) { - float w, wb; + float w; string a; w = self.weapon; @@ -355,11 +352,10 @@ void W_ThrowWeapon(vector velo, vector delta, float doreduce) if(!W_IsWeaponThrowable(w)) return; - wb = W_WeaponBit(w); - if(self.weapons & wb != wb) + if(!WEPSET_CONTAINS_EW(self, w)) return; + WEPSET_ANDNOT_EW(self, w); - self.weapons &~= wb; W_SwitchWeapon_Force(self, w_getbestweapon(self)); a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo); if not(a) @@ -459,8 +455,8 @@ void W_WeaponFrame() //if (self.button0) // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n"); - float wb; - wb = W_WeaponBit(self.weapon); + float w; + w = self.weapon; // call the think code which may fire the weapon // and do so multiple times to resolve framerate dependency issues if the @@ -470,18 +466,18 @@ void W_WeaponFrame() while (c < W_TICSPERFRAME) { c = c + 1; - if(wb && ((self.weapons & wb) == 0)) + if(w && !WEPSET_CONTAINS_EW(self, w)) { if(self.weapon == self.switchweapon) W_SwitchWeapon_Force(self, w_getbestweapon(self)); - wb = 0; + w = 0; } v_forward = fo; v_right = ri; v_up = up; - if(wb) + if(w) weapon_action(self.weapon, WR_THINK); else weapon_action(self.weapon, WR_GONETHINK); @@ -521,3 +517,20 @@ void W_WeaponFrame() self.currentammo = 1; #endif } + +string W_Apply_Weaponreplace(string in) +{ + float n = tokenize_console(in); + string out = ""; + float i; + for(i = 0; i < n; ++i) + { + string s = argv(i); + string r = cvar_string(strcat("g_weaponreplace_", s)); + if(r == "") + out = strcat(out, " ", s); + else if(r != "0") + out = strcat(out, " ", r); + } + return substring(out, 1, -1); +} diff --git a/qcsrc/server/cl_weaponsystem.qc b/qcsrc/server/cl_weaponsystem.qc index 4f496e56eb..402b692af1 100644 --- a/qcsrc/server/cl_weaponsystem.qc +++ b/qcsrc/server/cl_weaponsystem.qc @@ -69,6 +69,7 @@ vector W_HitPlotNormalizedUntransform(vector org, entity targ, vector screenforw // x = 0..1 relative to hitbox; y = 0..1 relative to hitbox; z = distance + mi = ma = targ.origin + 0.5 * (targ.mins + targ.maxs); for(i = 0; i < 2; ++i) for(j = 0; j < 2; ++j) for(k = 0; k < 2; ++k) { thisv = targ.origin; @@ -557,7 +558,6 @@ void CL_Weaponentity_Think() self.angles = '0 0 0'; float f; - f = 0; if (self.state == WS_RAISE && !intermission_running) { f = (self.owner.weapon_nextthink - time) * g_weaponratefactor / autocvar_g_balance_weaponswitchdelay; @@ -727,7 +727,7 @@ void Send_WeaponComplain (entity e, float wpn, string wpnname, float type) float client_hasweapon(entity cl, float wpn, float andammo, float complain) { - float weaponbit, f; + float f; entity oldself; if(time < self.hasweapon_complain_spam) @@ -741,8 +741,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) sprint(self, "Invalid weapon\n"); return FALSE; } - weaponbit = W_WeaponBit(wpn); - if (cl.weapons & weaponbit) + if (WEPSET_CONTAINS_EW(cl, wpn)) { if (andammo) { @@ -783,7 +782,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) { // DRESK - 3/16/07 // Report Proper Weapon Status / Modified Weapon Ownership Message - if(weaponsInMap & weaponbit) + if (WEPSET_CONTAINS_AW(weaponsInMap, wpn)) { sprint(cl, strcat("You do not have the ^2", W_Name(wpn), "\n") ); Send_WeaponComplain (cl, wpn, W_Name(wpn), 1); @@ -796,7 +795,7 @@ float client_hasweapon(entity cl, float wpn, float andammo, float complain) e = get_weaponinfo(wpn); s = e.model2; - for(e = world; (e = findfloat(e, weapons, weaponbit)); ) + for(e = world; (e = findfloat(e, weapon, wpn)); ) { if(e.classname == "droppedweapon") continue; @@ -867,10 +866,15 @@ void W_SwitchToOtherWeapon(entity pl) { // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) float w, ww; - w = W_WeaponBit(pl.weapon); - pl.weapons &~= w; - ww = w_getbestweapon(pl); - pl.weapons |= w; + w = pl.weapon; + if(WEPSET_CONTAINS_EW(pl, w)) + { + WEPSET_ANDNOT_EW(pl, w); + ww = w_getbestweapon(pl); + WEPSET_OR_EW(pl, w); + } + else + ww = w_getbestweapon(pl); if(ww) W_SwitchWeapon_Force(pl, ww); } @@ -1073,8 +1077,6 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float f { vector mdirection; float mspeed; - float outspeed; - float nstyle; vector outvelocity; mvelocity = mvelocity * g_weaponspeedfactor; @@ -1082,61 +1084,7 @@ vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float f mdirection = normalize(mvelocity); mspeed = vlen(mvelocity); - nstyle = autocvar_g_projectiles_newton_style; - if(nstyle == 0 || forceAbsolute) - { - // absolute velocity - outvelocity = mvelocity; - } - else if(nstyle == 1) - { - // true Newtonian projectiles - outvelocity = pvelocity + mvelocity; - } - else if(nstyle == 2) - { - // true Newtonian projectiles with automatic aim adjustment - // - // solve: |outspeed * mdirection - pvelocity| = mspeed - // outspeed^2 - 2 * outspeed * (mdirection * pvelocity) + pvelocity^2 - mspeed^2 = 0 - // outspeed = (mdirection * pvelocity) +- sqrt((mdirection * pvelocity)^2 - pvelocity^2 + mspeed^2) - // PLUS SIGN! - // not defined? - // then... - // pvelocity^2 - (mdirection * pvelocity)^2 > mspeed^2 - // velocity without mdirection component > mspeed - // fire at smallest possible mspeed that works? - // |(mdirection * pvelocity) * pvelocity - pvelocity| = mspeed - - vector solution; - solution = solve_quadratic(1, -2 * (mdirection * pvelocity), pvelocity * pvelocity - mspeed * mspeed); - if(solution_z) - outspeed = solution_y; // the larger one - else - { - //outspeed = 0; // slowest possible shot - outspeed = solution_x; // the real part (that is, the average!) - //dprint("impossible shot, adjusting\n"); - } - - outspeed = bound(mspeed * autocvar_g_projectiles_newton_style_2_minfactor, outspeed, mspeed * autocvar_g_projectiles_newton_style_2_maxfactor); - outvelocity = mdirection * outspeed; - } - else if(nstyle == 3) - { - // pseudo-Newtonian: - outspeed = mspeed + mdirection * pvelocity; - outspeed = bound(mspeed * 0.7, outspeed, mspeed * 5.0); - outvelocity = mdirection * outspeed; - } - else if(nstyle == 4) - { - // tZorkian: - outspeed = mspeed + vlen(pvelocity); - outvelocity = mdirection * outspeed; - } - else - error("g_projectiles_newton_style must be 0 (absolute), 1 (Newtonian), 2 (Newtonian + aimfix), 3 (pseudo Newtonian) or 4 (tZorkian)!"); + outvelocity = get_shotvelocity(pvelocity, mdirection, mspeed, (forceAbsolute ? 0 : autocvar_g_projectiles_newton_style), autocvar_g_projectiles_newton_style_2_minfactor, autocvar_g_projectiles_newton_style_2_maxfactor); return outvelocity; } diff --git a/qcsrc/server/command/banning.qc b/qcsrc/server/command/banning.qc index ab58a4aee3..eebadfa73b 100644 --- a/qcsrc/server/command/banning.qc +++ b/qcsrc/server/command/banning.qc @@ -112,11 +112,40 @@ void BanCommand_unban(float request, float argc) switch(request) { case CMD_REQUEST_COMMAND: - { + { if(argv(1)) { - Ban_Delete(stof(argv(1))); - return; + float tmp_number = -1; + string tmp_string; + + if(substring(argv(1), 0, 1) == "#") + { + tmp_string = substring(argv(1), 1, -1); + + if(tmp_string != "") // is it all one token? like #1 + { + tmp_number = stof(tmp_string); + } + else if(argc > 2) // no, it's two tokens? # 1 + { + tmp_number = stof(argv(2)); + } + else + tmp_number = -1; + } + else // maybe it's ONLY a number? + { + tmp_number = stof(argv(1)); + + if((tmp_number == 0) && (argv(1) != "0")) + { tmp_number = -1; } + } + + if(tmp_number >= 0) + { + Ban_Delete(tmp_number); + return; + } } } @@ -228,4 +257,4 @@ float BanCommand(string command) } return FALSE; -} \ No newline at end of file +} diff --git a/qcsrc/server/command/banning.qh b/qcsrc/server/command/banning.qh index 7a6138283f..8e3ecb7ba0 100644 --- a/qcsrc/server/command/banning.qh +++ b/qcsrc/server/command/banning.qh @@ -3,7 +3,7 @@ // Last updated: December 29th, 2011 // ===================================== -#define GET_BAN_ARG(v,d) if((argc > reason_arg) && ((v = stof(argv(reason_arg))) != 0)) ++reason_arg; else v = d +#define GET_BAN_ARG(v,d) if(argc > reason_arg) { if((v = stof(argv(reason_arg))) != 0) ++reason_arg; else v = d; } else v = d #define GET_BAN_REASON(v,d) if(argc > reason_arg) v = substring(command, argv_start_index(reason_arg), strlen(command) - argv_start_index(reason_arg)); else v = d void Ban_KickBanClient(entity client, float bantime, float masksize, string reason); @@ -12,4 +12,4 @@ float Ban_Insert(string ip, float bantime, string reason, float dosync); float Ban_Delete(float i); // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file -void BanCommand_macro_write_aliases(float fh) \ No newline at end of file +void BanCommand_macro_write_aliases(float fh) diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index e885c3054b..530646afd2 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -90,7 +90,7 @@ void ClientCommand_clientversion(float request, float argc) // internal command, self.version_mismatch = 1; ClientKill_TeamChange(-2); // observe } - else if(autocvar_g_campaign || autocvar_g_balance_teams || autocvar_g_balance_teams_force) + else if(autocvar_g_campaign || autocvar_g_balance_teams) { //JoinBestTeam(self, FALSE, TRUE); } @@ -226,40 +226,6 @@ void ClientCommand_ready(float request) // todo: anti-spam for toggling readynes } } -void ClientCommand_reportcvar(float request, float argc, string command) -{ - switch(request) - { - case CMD_REQUEST_COMMAND: - { - if(argv(1) != "") - { - float tokens; - string s; - - if(substring(argv(2), 0, 1) == "$") // undefined cvar: use the default value on the server then - { - s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\""); - tokens = tokenize_console(s); - } - - GetCvars(1); - - return; - } - } - - default: - sprint(self, "Incorrect parameters for ^2reportcvar^7\n"); - case CMD_REQUEST_USAGE: - { - sprint(self, "\nUsage:^3 cmd reportcvar \n"); - sprint(self, " Where 'cvar' is the cvar plus arguments to send to the server.\n"); - return; - } - } -} - void ClientCommand_say(float request, float argc, string command) { switch(request) @@ -324,7 +290,7 @@ void ClientCommand_selectteam(float request, float argc) case "pink": selection = COLOR_TEAM4; break; case "auto": selection = (-1); break; - default: break; + default: selection = 0; break; } if(selection) @@ -392,13 +358,13 @@ void ClientCommand_sentcvar(float request, float argc, string command) { if(argv(1) != "") { - float tokens; + //float tokens; string s; if(argc == 2) // undefined cvar: use the default value on the server then { s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\""); - tokens = tokenize_console(s); + tokenize_console(s); } GetCvars(1); @@ -595,7 +561,6 @@ void ClientCommand_(float request) CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(request, arguments), "Retrieve mapshot picture from the server") \ CLIENT_COMMAND("join", ClientCommand_join(request), "Become a player in the game") \ CLIENT_COMMAND("ready", ClientCommand_ready(request), "Qualify as ready to end warmup stage (or restart server if allowed)") \ - CLIENT_COMMAND("reportcvar", ClientCommand_reportcvar(request, arguments, command), "Old system for sending a client cvar to the server") \ CLIENT_COMMAND("say", ClientCommand_say(request, arguments, command), "Print a message to chat to all players") \ CLIENT_COMMAND("say_team", ClientCommand_say_team(request, arguments, command), "Print a message to chat to all team mates") \ CLIENT_COMMAND("selectteam", ClientCommand_selectteam(request, arguments), "Attempt to choose a team to join into") \ @@ -658,6 +623,10 @@ void ClientCommand_macro_write_aliases(float fh) void SV_ParseClientCommand(string command) { + // if we're banned, don't even parse the command + if(Ban_MaybeEnforceBanOnce(self)) + return; + float argc = tokenize_console(command); // for the mutator hook system @@ -679,7 +648,6 @@ void SV_ParseClientCommand(string command) case "mv_getpicture": break; // handled by server in this file case "pause": break; // handled by engine in host_cmd.c case "prespawn": break; // handled by engine in host_cmd.c - case "reportcvar": break; // handled by server in this file case "sentcvar": break; // handled by server in this file case "spawn": break; // handled by engine in host_cmd.c diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index 7bda0e0569..eae71b045c 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -63,6 +63,7 @@ entity GetIndexedEntity(float argc, float start_index) next_token = -1; index = start_index; + selection = world; if(argc > start_index) { @@ -80,6 +81,8 @@ entity GetIndexedEntity(float argc, float start_index) tmp_number = stof(argv(index)); ++index; } + else + tmp_number = 0; } else // maybe it's ONLY a number? { @@ -123,6 +126,7 @@ entity GetFilteredEntity(string input) } else { + selection = world; FOR_EACH_CLIENT(tmp_player) if (strdecolorize(tmp_player.netname) == strdecolorize(input)) selection = tmp_player; @@ -137,12 +141,12 @@ float GetFilteredNumber(string input) entity selection = GetFilteredEntity(input); float output; - if(selection) { output = num_for_edict(selection); } + output = num_for_edict(selection); 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) @@ -433,7 +437,7 @@ void CommonCommand_records(float request, entity caller) float i; for(i = 0; i < 10; ++i) - if(records_reply[i]) + if(records_reply[i] != "") print_to(caller, records_reply[i]); return; // never fall through to usage @@ -614,6 +618,7 @@ void CommonCommand_who(float request, entity caller, float argc) print_to(caller, sprintf(strreplace(" ", separator, " %-4s %-20s %-5s %-3s %-9s %-16s %s "), "ent", "nickname", "ping", "pl", "time", "ip", "crypto_id")); + total_listed_players = 0; FOR_EACH_CLIENT(tmp_player) { is_bot = (clienttype(tmp_player) == CLIENTTYPE_BOT); @@ -759,4 +764,4 @@ void CommonCommand_macro_write_aliases(float fh) #undef COMMON_COMMAND return; -} \ No newline at end of file +} diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index 3640c81728..0bedd6d5e2 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -14,6 +14,8 @@ string getrecords(float page) // 50 records per page { float rec = 0, r, i; string h, s; + + s = ""; if (g_ctf) { @@ -23,7 +25,7 @@ string getrecords(float page) // 50 records per page { r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time"))); - if (r == 0) + if not(r) continue; // TODO: uid2name @@ -42,7 +44,7 @@ string getrecords(float page) // 50 records per page { r = race_readTime(MapInfo_Map_bspname, 1); - if (r == 0) + if not(r) continue; h = race_readName(MapInfo_Map_bspname, 1); @@ -60,7 +62,7 @@ string getrecords(float page) // 50 records per page { r = race_readTime(MapInfo_Map_bspname, 1); - if (r == 0) + if not(r) continue; h = race_readName(MapInfo_Map_bspname, 1); @@ -72,7 +74,7 @@ string getrecords(float page) // 50 records per page MapInfo_ClearTemps(); - if (s == "" && page == 0) + if(s == "" && page == 0) return "No records are available on this server.\n"; else return s; @@ -85,6 +87,7 @@ string getrankings() map = GetMapname(); + s = ""; for (i = 1; i <= RANKINGS_CNT; ++i) { t = race_readTime(map, i); @@ -107,7 +110,7 @@ string getrankings() string getladder() { - float i, j, k, uidcnt, thiscnt; + float i, j, k, uidcnt = 0, thiscnt; string s, temp_s, rr, myuid, thisuid; if(g_cts) @@ -281,4 +284,4 @@ string getladder() return "No ladder on this server!\n"; else return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s); -} \ No newline at end of file +} diff --git a/qcsrc/server/command/radarmap.qc b/qcsrc/server/command/radarmap.qc index 16f3212eda..d865176296 100644 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@ -28,7 +28,7 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b) c = trace_endpos; } - n += tracebox_inverted(c, mi, ma, b, MOVE_WORLDONLY, world); + n += tracebox_inverted(c, mi, ma, b, MOVE_WORLDONLY, world, FALSE); white += vlen(trace_endpos - c); c = trace_endpos; @@ -424,4 +424,4 @@ float RadarMap_Make(float argc) } return FALSE; -} \ No newline at end of file +} diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index e70ce44ddb..25c7940cb7 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -81,6 +81,7 @@ void GameCommand_adminmsg(float request, float argc) float infobartime = stof(argv(3)); string successful, t; + successful = string_null; if((targets) && (admin_message)) { @@ -117,7 +118,7 @@ void GameCommand_adminmsg(float request, float argc) if(successful) bprint("Successfully sent message '", admin_message, "' to ", successful, ".\n"); else - print("No players given (", original_targets, ") could recieve the message.\n"); + print("No players given (", original_targets, ") could receive the message.\n"); return; } @@ -166,7 +167,7 @@ void GameCommand_allspec(float request, float argc) { entity client; string reason = argv(1); - float i; + float i = 0; FOR_EACH_REALPLAYER(client) { @@ -315,7 +316,7 @@ void GameCommand_bbox(float request) } } -void GameCommand_bot_cmd(float request, float argc) +void GameCommand_bot_cmd(float request, float argc, string command) { switch(request) { @@ -328,6 +329,17 @@ void GameCommand_bot_cmd(float request, float argc) bot_resetqueues(); return; } + else if(argv(1) == "setbots") + { + cvar_settemp("bot_vs_human", "0"); + cvar_settemp("minplayers", "0"); + cvar_settemp("bot_number", "0"); + bot_fixcount(); + cvar_settemp("bot_number", argv(2)); + if(!bot_fixcount()) + print("Sorry, could not set requested bot count\n"); + return; + } else if(argv(1) == "load" && argc == 3) { float fh, i; @@ -352,7 +364,10 @@ void GameCommand_bot_cmd(float request, float argc) } else if(argv(2) == "setbots") { + cvar_settemp("bot_vs_human", "0"); cvar_settemp("minplayers", "0"); + cvar_settemp("bot_number", "0"); + bot_fixcount(); cvar_settemp("bot_number", argv(3)); if(!bot_fixcount()) print("Sorry, could not set requested bot count\n"); @@ -364,7 +379,7 @@ void GameCommand_bot_cmd(float request, float argc) if(bot == world) bot = find_bot_by_name(argv(2)); if(bot) - bot_queuecommand(bot, strcat(argv(3), " ", argv(4))); + bot_queuecommand(bot, substring(s, argv_start_index(3), -1)); } } else @@ -391,8 +406,8 @@ void GameCommand_bot_cmd(float request, float argc) bot = find_bot_by_name(argv(1)); if(bot) { - print(strcat("Command '", strcat(argv(2), " ", argv(3)), "' sent to bot ", bot.netname, "\n")); - bot_queuecommand(bot, strcat(argv(2), " ", argv(3))); + print(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n")); + bot_queuecommand(bot, substring(command, argv_start_index(2), -1)); return; } else @@ -528,7 +543,7 @@ void GameCommand_defer_clear_all(float request) case CMD_REQUEST_COMMAND: { entity client; - float i; + float i = 0; float argc; FOR_EACH_CLIENT(client) @@ -808,6 +823,64 @@ void GameCommand_gettaginfo(float request, float argc) } } +void GameCommand_animbench(float request, float argc) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + entity tmp_entity; + float i; + vector v; + + if(argc >= 4) + { + tmp_entity = spawn(); + if(argv(1) == "w") + setmodel(tmp_entity, (nextent(world)).weaponentity.model); + else + { + precache_model(argv(1)); + setmodel(tmp_entity, argv(1)); + } + float f1 = stof(argv(2)); + float f2 = stof(argv(3)); + float t0; + float t1 = 0; + float t2 = 0; + float n = 0; + + while(t1 + t2 < 1) + { + tmp_entity.frame = f1; + t0 = gettime(GETTIME_HIRES); + getsurfacepoint(tmp_entity, 0, 0); + t1 += gettime(GETTIME_HIRES) - t0; + tmp_entity.frame = f2; + t0 = gettime(GETTIME_HIRES); + getsurfacepoint(tmp_entity, 0, 0); + t2 += gettime(GETTIME_HIRES) - t0; + n += 1; + } + print("model ", tmp_entity.model, " frame ", ftos(f1), " animtime ", ftos(n / t1), "/s\n"); + print("model ", tmp_entity.model, " frame ", ftos(f2), " animtime ", ftos(n / t2), "/s\n"); + + remove(tmp_entity); + return; + } + } + + default: + print("Incorrect parameters for ^2gettaginfo^7\n"); + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 sv_cmd gettaginfo model frame index [command one] [command two]\n"); + print("See also: ^2bbox, trace^7\n"); + return; + } + } +} + void GameCommand_gotomap(float request, float argc) { switch(request) @@ -904,6 +977,7 @@ void GameCommand_moveplayer(float request, float argc) string notify = argv(3); string successful, t; + successful = string_null; // lets see if the target(s) even actually exist. if((targets) && (destination)) @@ -1242,6 +1316,8 @@ void GameCommand_shuffleteams(float request) float i, x, z, t_teams, t_players, team_color; // count the total amount of players and total amount of teams + t_players = 0; + t_teams = 0; FOR_EACH_PLAYER(tmp_player) { CheckAllowedTeams(tmp_player); @@ -1347,7 +1423,7 @@ void GameCommand_stuffto(float request, float argc) { if(argv(2)) { - entity client = GetIndexedEntity(argc, 1)); + entity client = GetIndexedEntity(argc, 1); float accepted = VerifyClientEntity(client, TRUE, FALSE); if(accepted > 0) @@ -1421,7 +1497,6 @@ void GameCommand_trace(float request, float argc) { rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world); - tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world); if(trace_startsolid) { @@ -1656,8 +1731,9 @@ void GameCommand_(float request) SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \ 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("animbench", GameCommand_animbench(request, arguments), "Benchmark model animation (LAGS)") \ 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") \ @@ -1803,4 +1879,4 @@ void GameCommand(string command) print(((command != "") ? strcat("Unknown server command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try sv_cmd help.\n"); return; -} \ No newline at end of file +} diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 4cd4f11569..e456475300 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -191,12 +191,12 @@ void VoteCount(float first_count) vote_accept_count = vote_reject_count = vote_abstain_count = 0; float spectators_allowed = ((autocvar_sv_vote_nospectators != 2) - || ((autocvar_sv_vote_nospectators == 1) && inWarmupStage) + || ((autocvar_sv_vote_nospectators == 1) && (inWarmupStage || gameover)) || (autocvar_sv_vote_nospectators == 0)); - float vote_player_count, is_player, notvoters; - float vote_real_player_count, vote_real_accept_count; - float vote_real_reject_count, vote_real_abstain_count; + float vote_player_count = 0, is_player, notvoters = 0; + float vote_real_player_count = 0, vote_real_accept_count = 0; + float vote_real_reject_count = 0, vote_real_abstain_count = 0; float vote_needed_of_voted, final_needed_votes; float vote_factor_overall, vote_factor_of_voted; @@ -404,7 +404,7 @@ void ReadyCount() { entity tmp_player; float ready_needed_factor, ready_needed_count; - float t_ready, t_players; + float t_ready = 0, t_players = 0; FOR_EACH_REALPLAYER(tmp_player) { @@ -476,15 +476,6 @@ float VoteCommand_checkinlist(string vote_command, string list) if(strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return TRUE; - // if gotomap is allowed, chmap is too, and vice versa - if(vote_command == "gotomap") - if(strstrofs(l, " chmap ", 0) >= 0) - return TRUE; - - if(vote_command == "chmap") - if(strstrofs(l, " gotomap ", 0) >= 0) - return TRUE; - return FALSE; } @@ -516,30 +507,92 @@ string ValidateMap(string validated_map, entity caller) return validated_map; } -float VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc) +float VoteCommand_checkargs(float startpos, float argc) { - string first_command; - - first_command = argv(startpos); + float p, q, check, minargs; + string cvarname = strcat("sv_vote_command_restriction_", argv(startpos)); + string cmdrestriction = cvar_string(cvarname); // note: this warns on undefined cvar. We want that. + string charlist, arg; + float checkmate; - if not(VoteCommand_checkinlist(first_command, vote_list)) + if(cmdrestriction == "") + return TRUE; + + ++startpos; // skip command name + + // check minimum arg count + + // 0 args: argc == startpos + // 1 args: argc == startpos + 1 + // ... + + minargs = stof(cmdrestriction); + if(argc - startpos < minargs) return FALSE; - if(argc < startpos) // These commands won't work without arguments + p = strstrofs(cmdrestriction, ";", 0); // find first semicolon + + for(;;) { - switch(first_command) + // we know that at any time, startpos <= argc - minargs + // so this means: argc-minargs >= startpos >= argc, thus + // argc-minargs >= argc, thus minargs <= 0, thus all minargs + // have been seen already + + if(startpos >= argc) // all args checked? GOOD + break; + + if(p < 0) // no more args? FAIL { - case "map": - case "chmap": - case "gotomap": - case "kick": - case "kickban": - return FALSE; - - default: { break; } + // exception: exactly minargs left, this one included + if(argc - startpos == minargs) + break; + + // otherwise fail + return FALSE; + } + + // cut to next semicolon + q = strstrofs(cmdrestriction, ";", p+1); // find next semicolon + if(q < 0) + charlist = substring(cmdrestriction, p+1, -1); + else + charlist = substring(cmdrestriction, p+1, q - (p+1)); + + // in case we ever want to allow semicolons in VoteCommand_checknasty + // charlist = strreplace("^^", ";", charlist); + + if(charlist != "") + { + // verify the arg only contains allowed chars + arg = argv(startpos); + checkmate = strlen(arg); + for(check = 0; check < checkmate; ++check) + if(strstrofs(charlist, substring(arg, check, 1), 0) < 0) + return FALSE; // not allowed character + // all characters are allowed. FINE. } + + ++startpos; + --minargs; + p = q; } + + return TRUE; +} + +float VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc) +{ + string first_command; + first_command = argv(startpos); + + if not(VoteCommand_checkinlist(first_command, vote_list)) + return FALSE; + + if not(VoteCommand_checkargs(startpos, argc)) + return FALSE; + switch(first_command) // now go through and parse the proper commands to adjust as needed. { case "kick": @@ -635,7 +688,7 @@ void VoteCommand_call(float request, entity caller, float argc, string vote_comm || ((autocvar_sv_vote_nospectators == 1) && inWarmupStage) || (autocvar_sv_vote_nospectators == 0)); - float tmp_playercount; + float tmp_playercount = 0; entity tmp_player; vote_command = VoteCommand_extractcommand(vote_command, 2, argc); @@ -997,4 +1050,4 @@ void VoteCommand(float request, entity caller, float argc, string vote_command) return; } } -} \ No newline at end of file +} diff --git a/qcsrc/server/constants.qh b/qcsrc/server/constants.qh index b95c726136..aec1e3256b 100644 --- a/qcsrc/server/constants.qh +++ b/qcsrc/server/constants.qh @@ -142,11 +142,6 @@ float MSG_ENTITY = 5; // csqc float TE_BEAM = 13; // grappling hook -// CTF -float FLAG_BASE = 1; -float FLAG_CARRY = 2; -float FLAG_DROPPED = 3; - float COLOR_TEAM1 = 5; // red float COLOR_TEAM2 = 14; // blue float COLOR_TEAM3 = 13; // yellow diff --git a/qcsrc/server/csqcprojectile.qc b/qcsrc/server/csqcprojectile.qc index d5b6697f25..3554f1ff67 100644 --- a/qcsrc/server/csqcprojectile.qc +++ b/qcsrc/server/csqcprojectile.qc @@ -13,6 +13,7 @@ float CSQCProjectile_SendEntity(entity to, float sf) if(self.flags & FL_ONGROUND) sf |= 0x40; + ft = fr = 0; if(self.fade_time != 0 || self.fade_rate != 0) { ft = (self.fade_time - time) / sys_frametime; @@ -79,9 +80,9 @@ void CSQCProjectile(entity e, float clientanimate, float type, float docull) else e.gravity = 0; - e.csqcprojectile_type = type; if(!sound_allowed(MSG_BROADCAST, e)) type |= 0x80; + e.csqcprojectile_type = type; } void UpdateCSQCProjectile(entity e) diff --git a/qcsrc/server/ctf.qc b/qcsrc/server/ctf.qc deleted file mode 100644 index f5835377d9..0000000000 --- a/qcsrc/server/ctf.qc +++ /dev/null @@ -1,1212 +0,0 @@ -#define FLAG_MIN (PL_MIN + '0 0 -13') -#define FLAG_MAX (PL_MAX + '0 0 -13') - -.entity basewaypoint; -.entity sprite; -entity ctf_worldflaglist; // CTF flags in the map -.entity ctf_worldflagnext; -.float dropperid; -.float ctf_droptime; - -.float next_take_time; // the next time a player can pick up a flag (time + blah) - /// I used this, in part, to fix the looping score bug. - avirox -//float FLAGSCORE_PICKUP = 1; -//float FLAGSCORE_RETURN = 5; // returned by owner team -//float FLAGSCORE_RETURNROGUE = 10; // returned by rogue team -//float FLAGSCORE_CAPTURE = 5; - -#define FLAG_CARRY_POS '-15 0 7' - -.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture - -float captureshield_min_negscore; // punish at -20 points -float captureshield_max_ratio; // punish at most 30% of each team -float captureshield_force; // push force of the shield - -float ctf_captureshield_shielded(entity p) -{ - float s, se; - entity e; - float players_worseeq, players_total; - - if(captureshield_max_ratio <= 0) - return FALSE; - - s = PlayerScore_Add(p, SP_SCORE, 0); - if(s >= -captureshield_min_negscore) - return FALSE; - - players_total = players_worseeq = 0; - FOR_EACH_PLAYER(e) - { - if(e.team != p.team) - continue; - se = PlayerScore_Add(e, SP_SCORE, 0); - if(se <= s) - ++players_worseeq; - ++players_total; - } - - // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse - // use this rule here - - if(players_worseeq >= players_total * captureshield_max_ratio) - return FALSE; - - return TRUE; -} - -void ctf_captureshield_update(entity p, float dir) -{ - float should; - if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only - { - should = ctf_captureshield_shielded(p); - if(should != dir) - { - if(should) - { - Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0); - // TODO csqc notifier for this - } - else - { - Send_CSQC_Centerprint_Generic(p, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0); - // TODO csqc notifier for this - } - p.ctf_captureshielded = should; - } - } -} - -float ctf_captureshield_customize() -{ - if not(other.ctf_captureshielded) - return FALSE; - if(self.team == other.team) - return FALSE; - return TRUE; -} - -.float ctf_captureshield_touch_msgtime; -void ctf_captureshield_touch() -{ - if not(other.ctf_captureshielded) - return; - if(self.team == other.team) - return; - vector mymid; - vector othermid; - mymid = (self.absmin + self.absmax) * 0.5; - othermid = (other.absmin + other.absmax) * 0.5; - Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force); - if (time - other.ctf_captureshield_touch_msgtime > 2) - Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0); - other.ctf_captureshield_touch_msgtime = time; -} - -void ctf_flag_spawnstuff() -{ - entity e; - e = spawn(); - e.enemy = self; - e.team = self.team; - e.touch = ctf_captureshield_touch; - e.customizeentityforclient = ctf_captureshield_customize; - e.classname = "ctf_captureshield"; - e.effects = EF_ADDITIVE; - e.movetype = MOVETYPE_NOCLIP; - e.solid = SOLID_TRIGGER; - e.avelocity = '7 0 11'; - setorigin(e, self.origin); - setmodel(e, "models/ctf/shield.md3"); - e.scale = 0.5; - setsize(e, e.scale * e.mins, e.scale * e.maxs); - - waypoint_spawnforitem_force(self, self.origin); - self.nearestwaypointtimeout = 0; // activate waypointing again - self.basewaypoint = self.nearestwaypoint; - - if(self.team == COLOR_TEAM1) - WaypointSprite_SpawnFixed("redbase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM1 - 1, FALSE)); - else - WaypointSprite_SpawnFixed("bluebase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM2 - 1, FALSE)); -} - -float ctf_score_value(string parameter) -{ - return cvar(strcat("g_ctf_personal", parameter)); -} - -void FakeTimeLimit(entity e, float t) -{ - msg_entity = e; - WriteByte(MSG_ONE, 3); // svc_updatestat - WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT - if(t < 0) - WriteCoord(MSG_ONE, autocvar_timelimit); - else - WriteCoord(MSG_ONE, (t + 1) / 60); -} - -float flagcaptimerecord; -.float flagpickuptime; -//.float iscommander; -//.float ctf_state; - -void() FlagThink; -void() FlagTouch; - -void place_flag() -{ - if(self.classname != "item_flag_team") - { - backtrace("PlaceFlag a non-flag"); - return; - } - - setattachment(self, world, ""); - self.mdl = self.model; - self.flags = FL_ITEM | FL_NOTARGET; - self.solid = SOLID_TRIGGER; - self.movetype = MOVETYPE_NONE; - self.velocity = '0 0 0'; - self.origin_z = self.origin_z + 6; - self.think = FlagThink; - self.touch = FlagTouch; - self.nextthink = time + 0.1; - self.cnt = FLAG_BASE; - self.mangle = self.angles; - self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - //self.effects = self.effects | EF_DIMLIGHT; - if(self.noalign) - { - self.dropped_origin = self.origin; - } - else - { - droptofloor(); - self.movetype = MOVETYPE_TOSS; - } - - InitializeEntity(self, ctf_flag_spawnstuff, INITPRIO_SETLOCATION); -} - -void LogCTF(string mode, float flagteam, entity actor) -{ - string s; - if(!autocvar_sv_eventlog) - return; - s = strcat(":ctf:", mode); - s = strcat(s, ":", ftos(flagteam)); - if(actor != world) - s = strcat(s, ":", ftos(actor.playerid)); - GameLogEcho(s); -} - -void RegenFlag(entity e) -{ - if(e.classname != "item_flag_team") - { - backtrace("RegenFlag a non-flag"); - return; - } - - if(e.waypointsprite_attachedforcarrier) - WaypointSprite_DetachCarrier(e); - - setattachment(e, world, ""); - e.damageforcescale = 0; - e.takedamage = DAMAGE_NO; - e.movetype = MOVETYPE_NONE; - if(!e.noalign) - e.movetype = MOVETYPE_TOSS; - e.velocity = '0 0 0'; - e.solid = SOLID_TRIGGER; - // TODO: play a sound here - setorigin(e, e.dropped_origin); - e.angles = e.mangle; - e.cnt = FLAG_BASE; - e.owner = world; - e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk -} - -void ReturnFlag(entity e) -{ - if(e.classname != "item_flag_team") - { - backtrace("ReturnFlag a non-flag"); - return; - } - - if (e.owner) - if (e.owner.flagcarried == e) - { - WaypointSprite_DetachCarrier(e.owner); - e.owner.flagcarried = world; - - if(e.speedrunning) - FakeTimeLimit(e.owner, -1); - } - e.owner = world; - RegenFlag(e); -} - -void DropFlag(entity e, entity penalty_receiver, entity attacker) -{ - entity p; - - if(e.classname != "item_flag_team") - { - backtrace("DropFlag a non-flag"); - return; - } - - if(e.speedrunning) - { - ReturnFlag(e); - return; - } - - if (!e.owner) - { - dprint("FLAG: drop - no owner?!?!\n"); - return; - } - p = e.owner; - if (p.flagcarried != e) - { - dprint("FLAG: drop - owner is not carrying this flag??\n"); - return; - } - //bprint(p.netname, "^7 lost the ", e.netname, "\n"); - Send_KillNotification (p.netname, e.netname, "", INFO_LOSTFLAG, MSG_INFO); - - if(penalty_receiver) - UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop")); - else - UpdateFrags(p, -ctf_score_value("penalty_drop")); - PlayerScore_Add(p, SP_CTF_DROPS, +1); - ctf_captureshield_update(p, 0); // shield only - e.playerid = attacker.playerid; - e.ctf_droptime = time; - WaypointSprite_Spawn("flagdropped", 0, 0, e, '0 0 1' * 61, world, COLOR_TEAM1 + COLOR_TEAM2 - e.team, e, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAG, '0 1 1'); - WaypointSprite_Ping(e.waypointsprite_attachedforcarrier); - - if(p.waypointsprite_attachedforcarrier) - { - WaypointSprite_DetachCarrier(p); - } - else - { - bprint("\{1}^1Flag carrier had no flag sprite?!?\n"); - backtrace("Flag carrier had no flag sprite?!?"); - } - LogCTF("dropped", p.team, p); - sound (p, CH_TRIGGER, self.noise4, VOL_BASE, ATTN_NONE); - - setattachment(e, world, ""); - e.damageforcescale = autocvar_g_balance_ctf_damageforcescale; - e.takedamage = DAMAGE_YES; - - if (p.flagcarried == e) - p.flagcarried = world; - e.owner = world; - - e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk - e.solid = SOLID_TRIGGER; - e.movetype = MOVETYPE_TOSS; - // setsize(e, '-16 -16 0', '16 16 74'); - setorigin(e, p.origin - '0 0 24' + '0 0 37'); - e.cnt = FLAG_DROPPED; - e.velocity = '0 0 300'; - e.pain_finished = time + autocvar_g_ctf_flag_returntime;//30; - - trace_startsolid = FALSE; - tracebox(e.origin, e.mins, e.maxs, e.origin, TRUE, e); - if(trace_startsolid) - dprint("FLAG FALLTHROUGH will happen SOON\n"); -} - -void FlagThink() -{ - entity e; - - self.nextthink = time + 0.1; - - // sorry, we have to reset the flag size if it got squished by something - if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) - { - // if we can grow back, grow back - tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self); - if(!trace_startsolid) - setsize(self, FLAG_MIN, FLAG_MAX); - } - - if(self == ctf_worldflaglist) // only for the first flag - { - FOR_EACH_CLIENT(e) - ctf_captureshield_update(e, 1); // release shield only - } - - if(self.speedrunning) - if(self.cnt == FLAG_CARRY) - { - if(self.owner) - if(flagcaptimerecord) - if(time >= self.flagpickuptime + flagcaptimerecord) - { - bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n"); - - sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE); - self.owner.impulse = 141; // returning! - - e = self; - self = self.owner; - ReturnFlag(e); - ImpulseCommands(); - self = e; - return; - } - } - - if (self.cnt == FLAG_BASE) - return; - - if (self.cnt == FLAG_DROPPED) - { - // flag fallthrough? FIXME remove this if bug is really fixed now - if(self.origin_z < -131072) - { - dprint("FLAG FALLTHROUGH just happened\n"); - self.pain_finished = 0; - } - setattachment(self, world, ""); - if (time > self.pain_finished) - { - bprint("The ", self.netname, " has returned to base\n"); - sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE); - LogCTF("returned", self.team, world); - ReturnFlag(self); - } - return; - } - - e = self.owner; - if (e.classname != "player" || (e.deadflag) || (e.flagcarried != self)) - { - dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n"); - DropFlag(self, world, world); - return; - } -} - -float ctf_usekey() -{ - if(self.flagcarried) - { - DropFlag(self.flagcarried, self, world); - return TRUE; - } - return FALSE; -} - -void flag_cap_ring_spawn(vector org) -{ - shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1); -} - -void FlagTouch() -{ - if(gameover) return; - - float t; - entity player; - string s, s0, h0, h1; - if (other.classname != "player") - return; - if (other.health < 1) // ignore dead players - return; - - if (self.cnt == FLAG_CARRY) - 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; -} -*/ diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index a46c8f7e71..d7772e5090 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -16,14 +16,10 @@ noref float require_spawnfunc_prefix; // if this float exists, only functions wi // Globals -float ctf_score_value(string parameter); - float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_midair, g_minstagib, g_pinata, g_norecoil, g_minstagib_invis_alpha, g_bloodloss; float g_warmup_limit; float g_warmup_allguns; float g_warmup_allow_timeout; -float g_ctf_ignore_frags; -float g_ctf_reverse; float g_race_qualifying; float inWarmupStage; float g_pickup_respawntime_weapon; @@ -100,6 +96,7 @@ float maxclients; .float t_length, t_width; .vector destvec; // for rain +.vector destvec2; // for train .float cnt; // for rain .float count; //.float cnt2; @@ -200,6 +197,9 @@ void setanim(entity e, vector anim, float looping, float override, float restart .float watersound_finished; .float iscreature; .float damagedbycontents; +.float damagedbytriggers; +.float pushable; +.float teleportable; .vector oldvelocity; .float pauseregen_finished; @@ -276,6 +276,7 @@ float blockSpectators; //if set, new or existing spectators or observers will be .float spectatortime; //point in time since the client is spectating or observing void checkSpectatorBlock(); +float game_completion_ratio; // 0 at start, 1 near end .float winning; .float jointime; // time of joining .float alivetime; // time of being alive @@ -341,7 +342,6 @@ string gamemode_name; float startitem_failed; -void DropFlag(entity flag, entity penalty_receiver, entity attacker); void DropAllRunes(entity pl); @@ -349,13 +349,13 @@ typedef .float floatfield; floatfield Item_CounterField(float it); float W_AmmoItemCode(float wpn); -float W_WeaponBit(float wpn); string W_Name(float weaponid); +string W_Apply_Weaponreplace(string in); void FixIntermissionClient(entity e); void FixClientCvars(entity e); -float weaponsInMap; +WEPSET_DECLARE_A(weaponsInMap); .float respawn_countdown; // next number to count @@ -490,7 +490,6 @@ float independent_players; string clientstuff; .float phase; -.float weapons; .float pressedkeys; .float porto_forbidden; @@ -569,7 +568,6 @@ float servertime, serverprevtime, serverframetime; string matchid; .float hitplotfh; -.string noise4; .float last_pickup; @@ -649,6 +647,8 @@ float serverflags; .entity muzzle_flash; .float misc_bulletcounter; // replaces uzi & hlac bullet counter. +.float stat_respawn_time; // shows respawn time, and is negative when awaiting respawn + void PlayerUseKey(); typedef vector(entity player, entity spot, vector current) spawn_evalfunc_t; @@ -657,3 +657,20 @@ typedef vector(entity player, entity spot, vector current) spawn_evalfunc_t; .entity conveyor; string modname; + +.float missile_flags; +#define MIF_SPLASH 2 +#define MIF_ARC 4 +#define MIF_PROXY 8 +#define MIF_GUIDED_MANUAL 16 +#define MIF_GUIDED_HEAT 32 +#define MIF_GUIDED_LASER 64 +#define MIF_GUIDED_AI 128 +#define MIF_GUIDED_TAG 128 +#define MIF_GUIDED_ALL (MIF_GUIDED_MANUAL | MIF_GUIDED_HEAT | MIF_GUIDED_LASER | MIF_GUIDED_AI | MIF_GUIDED_TAG) +#define MIF_GUIDED_TRACKING (MIF_GUIDED_HEAT | MIF_GUIDED_LASER | MIF_GUIDED_AI | MIF_GUIDED_TAG) +#define MIF_GUIDED_CONFUSABLE (MIF_GUIDED_HEAT | MIF_GUIDED_AI) + +#define MISSILE_IS_CONFUSABLE(m) ((m.missile_flags & MIF_GUIDED_CONFUSABLE) ? TRUE : FALSE) +#define MISSILE_IS_GUIDED(m) ((m.missile_flags & MIF_GUIDED_ALL) ? TRUE : FALSE) +#define MISSILE_IS_TRACKING(m) ((m.missile_flags & MIF_GUIDED_TRACKING) ? TRUE : FALSE) diff --git a/qcsrc/server/domination.qc b/qcsrc/server/domination.qc index 9e56023cb5..0a2fe02b66 100644 --- a/qcsrc/server/domination.qc +++ b/qcsrc/server/domination.qc @@ -395,196 +395,6 @@ void dom_controlpoint_setup() -// player has joined game, get him on a team -// depreciated -/*void dom_player_join_team(entity pl) -{ - entity head; - float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam; - float balance_teams, force_balance, balance_type; - - balance_teams = autocvar_g_balance_teams; - balance_teams = autocvar_g_balance_teams_force; - - c1 = c2 = c3 = c4 = -1; - totalteams = 0; - - // first find out what teams are allowed - head = find(world, classname, "dom_team"); - while(head) - { - if(head.netname != "") - { - //if(head.team == pl.team) - // selected = head; - if(head.team == COLOR_TEAM1) - { - c1 = 0; - } - if(head.team == COLOR_TEAM2) - { - c2 = 0; - } - if(head.team == COLOR_TEAM3) - { - c3 = 0; - } - if(head.team == COLOR_TEAM4) - { - c4 = 0; - } - } - head = find(head, classname, "dom_team"); - } - - // make sure there are at least 2 teams to join - if(c1 >= 0) - totalteams = totalteams + 1; - if(c2 >= 0) - totalteams = totalteams + 1; - if(c3 >= 0) - totalteams = totalteams + 1; - if(c4 >= 0) - totalteams = totalteams + 1; - - if(totalteams <= 1) - error("dom_player_join_team: Too few teams available for domination\n"); - - // whichever teams that are available are set to 0 instead of -1 - - // if we don't care what team he ends up on, put him on whatever team he entered as. - // if he's not on a valid team, then put him on the smallest team - if(!balance_teams && !force_balance) - { - if( c1 >= 0 && pl.team == COLOR_TEAM1) - selectedteam = pl.team; - else if(c2 >= 0 && pl.team == COLOR_TEAM2) - selectedteam = pl.team; - else if(c3 >= 0 && pl.team == COLOR_TEAM3) - selectedteam = pl.team; - else if(c4 >= 0 && pl.team == COLOR_TEAM4) - selectedteam = pl.team; - else - selectedteam = -1; - if(selectedteam > 0) - { - SetPlayerColors(pl, selectedteam - 1); - return; - } - // otherwise end up on the smallest team (handled below) - } - - // now count how many players are on each team already - - head = find(world, classname, "player"); - while(head) - { - //if(head.netname != "") - { - if(head.team == COLOR_TEAM1) - { - if(c1 >= 0) - c1 = c1 + 1; - } - if(head.team == COLOR_TEAM2) - { - if(c2 >= 0) - c2 = c2 + 1; - } - if(head.team == COLOR_TEAM3) - { - if(c3 >= 0) - c3 = c3 + 1; - } - if(head.team == COLOR_TEAM4) - { - if(c4 >= 0) - c4 = c4 + 1; - } - } - head = find(head, classname, "player"); - } - - // c1...c4 now have counts of each team - // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker - - smallestteam = 0; - smallestteam_count = 999; - - // 2 gives priority to what team you're already on, 1 goes in order - balance_type = 1; - - if(balance_type == 1) - { - if(c1 >= 0 && c1 < smallestteam_count) - { - smallestteam = 1; - smallestteam_count = c1; - } - if(c2 >= 0 && c2 < smallestteam_count) - { - smallestteam = 2; - smallestteam_count = c2; - } - if(c3 >= 0 && c3 < smallestteam_count) - { - smallestteam = 3; - smallestteam_count = c3; - } - if(c4 >= 0 && c4 < smallestteam_count) - { - smallestteam = 4; - smallestteam_count = c4; - } - } - else - { - if(c1 >= 0 && (c1 < smallestteam_count || - (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) ) - { - smallestteam = 1; - smallestteam_count = c1; - } - if(c2 >= 0 && c2 < (c2 < smallestteam_count || - (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) ) - { - smallestteam = 2; - smallestteam_count = c2; - } - if(c3 >= 0 && c3 < (c3 < smallestteam_count || - (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) ) - { - smallestteam = 3; - smallestteam_count = c3; - } - if(c4 >= 0 && c4 < (c4 < smallestteam_count || - (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) ) - { - smallestteam = 4; - smallestteam_count = c4; - } - } - - if(smallestteam == 1) - { - selectedteam = COLOR_TEAM1 - 1; - } - if(smallestteam == 2) - { - selectedteam = COLOR_TEAM2 - 1; - } - if(smallestteam == 3) - { - selectedteam = COLOR_TEAM3 - 1; - } - if(smallestteam == 4) - { - selectedteam = COLOR_TEAM4 - 1; - } - - SetPlayerColors(pl, selectedteam); -} -*/ /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32) Control point for Domination gameplay. */ diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index f80e3e133c..1e7d2815d5 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -55,6 +55,7 @@ float damage_headshotbonus; // bonus multiplier for head shots, set to 0 after u .float teamkill_soundtime; .entity teamkill_soundsource; .entity pusher; +.float istypefrag; .float taunt_soundtime; @@ -101,10 +102,9 @@ void UpdateFrags(entity player, float f) // NOTE: f=0 means still count as a (positive) kill, but count no frags for it void W_SwitchWeapon_Force(entity e, float w); +entity GiveFrags_randomweapons; void GiveFrags (entity attacker, entity targ, float f, float deathtype) { - float w; - // TODO route through PlayerScores instead if(gameover) return; @@ -141,34 +141,44 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype) // after a frag, exchange the current weapon (or the culprit, if detectable) by a new random weapon float culprit; culprit = DEATH_WEAPONOF(deathtype); - if(!culprit || !(attacker.weapons & W_WeaponBit(culprit))) + if(!culprit) + culprit = attacker.weapon; + else if(!WEPSET_CONTAINS_EW(attacker, culprit)) culprit = attacker.weapon; - if(g_weaponarena_random_with_laser && culprit == WEPBIT_LASER) + if(g_weaponarena_random_with_laser && culprit == WEP_LASER) { // no exchange } else { + if(!GiveFrags_randomweapons) + { + GiveFrags_randomweapons = spawn(); + GiveFrags_randomweapons.classname = "GiveFrags_randomweapons"; + } + if(inWarmupStage) - w = warmup_start_weapons; + WEPSET_COPY_EA(GiveFrags_randomweapons, warmup_start_weapons); else - w = start_weapons; + WEPSET_COPY_EA(GiveFrags_randomweapons, start_weapons); // all others (including the culprit): remove - w &~= attacker.weapons; + WEPSET_ANDNOT_EE(GiveFrags_randomweapons, attacker); + WEPSET_ANDNOT_EW(GiveFrags_randomweapons, culprit); // among the remaining ones, choose one by random - w = randombits(w, 1, FALSE); - if(w) + W_RandomWeapons(GiveFrags_randomweapons, 1); + + if(!WEPSET_EMPTY_E(GiveFrags_randomweapons)) { - attacker.weapons |= w; - attacker.weapons &~= W_WeaponBit(culprit); + WEPSET_OR_EE(attacker, GiveFrags_randomweapons); + WEPSET_ANDNOT_EW(attacker, culprit); } } // after a frag, choose another random weapon set - if not(attacker.weapons & W_WeaponBit(attacker.weapon)) + if not(WEPSET_CONTAINS_EW(attacker, attacker.weapon)) W_SwitchWeapon_Force(attacker, w_getbestweapon(attacker)); } @@ -209,11 +219,6 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype) } f = 0; } - else if(g_ctf) - { - if(g_ctf_ignore_frags) - f = 0; - } } attacker.totalfrags += f; @@ -224,10 +229,10 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype) string Obituary_ExtraFragInfo(entity player) // Extra fragmessage information { - string health_output; - string ping_output; - string handicap_output; - string output; + string health_output = string_null; + string ping_output = string_null; + string handicap_output = string_null; + string output = string_null; if(autocvar_sv_fraginfo && ((autocvar_sv_fraginfo == 2) || inWarmupStage)) { @@ -345,6 +350,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) { if (deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE) msg = ColoredTeamName(targ.team); // TODO: check if needed? + else + msg = ""; if(!g_cts) // no "killed your own dumb self" message in CTS Send_CSQC_KillCenterprint(targ, msg, "", deathtype, MSG_SUICIDE); @@ -356,6 +363,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) if (targ.killcount > 2) msg = ftos(targ.killcount); + else + msg = ""; if(teamplay && deathtype == DEATH_MIRRORDAMAGE) { if(attacker.team == COLOR_TEAM1) @@ -379,9 +388,10 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) Send_CSQC_KillCenterprint(attacker, s, "", type, MSG_KILL); - if (targ.killcount > 2) { + if (targ.killcount > 2) msg = ftos(targ.killcount); - } + else + msg = ""; if (attacker.killcount > 2) { msg = ftos(attacker.killcount); @@ -406,7 +416,7 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) PlayerStats_Event(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1); } - if(targ.BUTTON_CHAT) { + if(targ.istypefrag) { Send_CSQC_KillCenterprint(attacker, s, Obituary_ExtraFragInfo(targ), KILL_TYPEFRAG, MSG_KILL); Send_CSQC_KillCenterprint(targ, a, Obituary_ExtraFragInfo(attacker), KILL_TYPEFRAGGED, MSG_KILL); } else { @@ -416,25 +426,19 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) attacker.taunt_soundtime = time + 1; - // TODO: fix this? - if (deathtype == DEATH_CUSTOM) + if (deathtype == DEATH_HURTTRIGGER && inflictor.message2 != "") + msg = inflictor.message2; + else if (deathtype == DEATH_CUSTOM) msg = deathmessage; else - msg = inflictor.message2; + msg = ""; if(strstrofs(msg, "%", 0) < 0) msg = strcat("%s ", msg, " by %s"); 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); @@ -442,10 +446,7 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) attacker.killcount = attacker.killcount + 1; - if (attacker.killcount > 2) { - Send_KillNotification(a, ftos(attacker.killcount), "", KILL_SPREE, MSG_SPREE); - } - else if (attacker.killcount == 3) + if (attacker.killcount == 3) { Send_KillNotification(a, "", "", KILL_SPREE_3, MSG_SPREE); AnnounceTo(attacker, "03kills"); @@ -487,6 +488,9 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) AnnounceTo(attacker, "30kills"); PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30, 1); } + else if (attacker.killcount > 2) { + Send_KillNotification(a, ftos(attacker.killcount), "", KILL_SPREE, MSG_SPREE); + } LogDeath("frag", deathtype, attacker, targ); } } @@ -497,6 +501,8 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype) msg = inflictor.message; else if (deathtype == DEATH_CUSTOM) msg = deathmessage; + else + msg = ""; if(strstrofs(msg, "%", 0) < 0) msg = strcat("%s ", msg); @@ -630,21 +636,17 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float if(autocvar_g_mirrordamage_virtual) { - vector v; - v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage); - v_z = 0; // fteqcc sucks + 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; } if(autocvar_g_friendlyfire_virtual) { - vector v; - v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage); - v_z = 0; // fteqcc sucks + vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage); targ.dmg_take += v_x; targ.dmg_save += v_y; targ.dmg_inflictor = inflictor; @@ -750,15 +752,6 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself } - // CTF: reduce damage/force - if(g_ctf) - if(targ == attacker) - if(targ.flagcarried) - { - damage = damage * autocvar_g_ctf_flagcarrier_selfdamage; - force = force * autocvar_g_ctf_flagcarrier_selfforce; - } - if(g_runematch) { // apply strength rune @@ -799,7 +792,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float if(targ.takedamage == DAMAGE_AIM) if(targ != attacker) { - if(damage_headshotbonus > 0) + if(damage_headshotbonus) { if(targ.classname == "player") { @@ -820,7 +813,8 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype |= HITTYPE_HEADSHOT; } if(deathtype & HITTYPE_HEADSHOT) - damage *= 1 + damage_headshotbonus; + if(damage_headshotbonus > 0) + damage *= 1 + damage_headshotbonus; } entity victim; @@ -860,8 +854,6 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float if(deathtype & HITTYPE_HEADSHOT) headshot = 1; } - if(g_ca) - PlayerScore_Add(attacker, SP_SCORE, damage * autocvar_g_ca_damage2score_multiplier); } } else @@ -973,16 +965,11 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float float RadiusDamage_running; float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity) -// Returns total damage applies to creatures + // Returns total damage applies to creatures { entity targ; - float finaldmg; - float power; vector blastorigin; vector force; - vector diff; - vector center; - vector nearest; float total_damage_to_creatures; entity next; float tfloordmg; @@ -1005,18 +992,18 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e total_damage_to_creatures = 0; if(deathtype != (WEP_HOOK | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once - if(DEATH_WEAPONOF(deathtype) != WEP_TUBA) // do not send tuba damage (bandwidth hog) - { - force = inflictor.velocity; - if(vlen(force) == 0) - force = '0 0 -1'; - else - force = normalize(force); - if(forceintensity >= 0) - Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker); - else - Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker); - } + if(DEATH_WEAPONOF(deathtype) != WEP_TUBA) // do not send tuba damage (bandwidth hog) + { + force = inflictor.velocity; + if(vlen(force) == 0) + force = '0 0 -1'; + else + force = normalize(force); + if(forceintensity >= 0) + Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker); + else + Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker); + } stat_damagedone = 0; @@ -1027,6 +1014,10 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e if (targ != inflictor) if (ignore != targ) if(targ.takedamage) { + vector nearest; + vector diff; + float power; + // LordHavoc: measure distance to nearest point on target (not origin) // (this guarentees 100% damage on a touch impact) nearest = targ.WarpZone_findradius_nearest; @@ -1040,6 +1031,7 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e // print(ftos(power), "\n"); if (power > 0) { + float finaldmg; if (power > 1) power = 1; finaldmg = coredamage * power + edgedamage * (1 - power); @@ -1047,95 +1039,125 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e { float a; float c; - float hits; - float total; - float hitratio; vector hitloc; vector myblastorigin; + vector center; + myblastorigin = WarpZone_TransformOrigin(targ, blastorigin); - center = targ.origin + (targ.mins + targ.maxs) * 0.5; + // if it's a player, use the view origin as reference - if (targ.classname == "player") - center = targ.origin + targ.view_ofs; + center = CENTER_OR_VIEWOFS(targ); + force = normalize(center - myblastorigin); force = force * (finaldmg / coredamage) * forceintensity; - // test line of sight to multiple positions on box, - // and do damage if any of them hit - hits = 0; - if (targ.classname == "player") - total = ceil(bound(1, finaldmg, 50)); - else - total = ceil(bound(1, finaldmg/10, 5)); hitloc = nearest; - c = 0; - while (c < total) + + if(targ != directhitentity) { - //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor); - WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor); - if (trace_fraction == 1 || trace_ent == targ) + float hits; + float total; + float hitratio; + float mininv_f, mininv_d; + + // test line of sight to multiple positions on box, + // and do damage if any of them hit + hits = 0; + + // we know: max stddev of hitratio = 1 / (2 * sqrt(n)) + // so for a given max stddev: + // n = (1 / (2 * max stddev of hitratio))^2 + + mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev; + mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev; + + if(autocvar_g_throughfloor_debug) + print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f)); + + total = 0.25 * pow(max(mininv_f, mininv_d), 2); + + if(autocvar_g_throughfloor_debug) + print(sprintf(" steps=%f", total)); + + if (targ.classname == "player") + total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player)); + else + total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other)); + + if(autocvar_g_throughfloor_debug) + print(sprintf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)))); + + for(c = 0; c < total; ++c) { - hits = hits + 1; - if (hits > 1) - hitloc = hitloc + nearest; - else - hitloc = nearest; + //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor); + WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor); + if (trace_fraction == 1 || trace_ent == targ) + { + ++hits; + if (hits > 1) + hitloc = hitloc + nearest; + else + hitloc = nearest; + } + nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x; + nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y; + nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z; } - nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x; - nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y; - nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z; - c = c + 1; + + nearest = hitloc * (1 / max(1, hits)); + hitratio = (hits / total); + a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1); + finaldmg = finaldmg * a; + a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1); + force = force * a; + + if(autocvar_g_throughfloor_debug) + print(sprintf(" D=%f F=%f\n", finaldmg, vlen(force))); } - nearest = hitloc * (1 / max(1, hits)); - hitratio = (hits / total); - a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1); - finaldmg = finaldmg * a; - a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1); - force = force * a; // laser force adjustments :P if(DEATH_WEAPONOF(deathtype) == WEP_LASER) { - if (targ == attacker) - { - vector vel; - - float force_zscale; - float force_velocitybiasramp; - float force_velocitybias; - - force_velocitybiasramp = autocvar_sv_maxspeed; - if(deathtype & HITTYPE_SECONDARY) - { - force_zscale = autocvar_g_balance_laser_secondary_force_zscale; - force_velocitybias = autocvar_g_balance_laser_secondary_force_velocitybias; - } - else - { - force_zscale = autocvar_g_balance_laser_primary_force_zscale; - force_velocitybias = autocvar_g_balance_laser_primary_force_velocitybias; - } - - vel = targ.velocity; - vel_z = 0; - vel = normalize(vel) * bound(0, vlen(vel) / force_velocitybiasramp, 1) * force_velocitybias; - force = - vlen(force) - * - normalize(normalize(force) + vel); - - force_z *= force_zscale; - } - else - { - if(deathtype & HITTYPE_SECONDARY) - { - force *= autocvar_g_balance_laser_secondary_force_other_scale; - } - else - { - force *= autocvar_g_balance_laser_primary_force_other_scale; - } - } + if (targ == attacker) + { + vector vel; + + float force_zscale; + float force_velocitybiasramp; + float force_velocitybias; + + force_velocitybiasramp = autocvar_sv_maxspeed; + if(deathtype & HITTYPE_SECONDARY) + { + force_zscale = autocvar_g_balance_laser_secondary_force_zscale; + force_velocitybias = autocvar_g_balance_laser_secondary_force_velocitybias; + } + else + { + force_zscale = autocvar_g_balance_laser_primary_force_zscale; + force_velocitybias = autocvar_g_balance_laser_primary_force_velocitybias; + } + + vel = targ.velocity; + vel_z = 0; + vel = normalize(vel) * bound(0, vlen(vel) / force_velocitybiasramp, 1) * force_velocitybias; + force = + vlen(force) + * + normalize(normalize(force) + vel); + + force_z *= force_zscale; + } + else + { + if(deathtype & HITTYPE_SECONDARY) + { + force *= autocvar_g_balance_laser_secondary_force_other_scale; + } + else + { + force *= autocvar_g_balance_laser_primary_force_other_scale; + } + } } //if (targ == attacker) @@ -1144,7 +1166,7 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e // print(" finaldmg ", ftos(finaldmg), " force ", vtos(force)); // print(" (", ftos(a), ")\n"); //} - if(hits || tfloordmg || tfloorforce) + if(finaldmg || vlen(force)) { if(targ.iscreature) { @@ -1222,43 +1244,59 @@ float Fire_AddDamage(entity e, entity o, float d, float t, float dt) if(maxtime > mintime || maxdps > mindps) { + // Constraints: + + // damage we have right now mindamage = mindps * mintime; - maxdamage = mindamage + d; - - // interval [mintime, maxtime] * [mindps, maxdps] - // intersected with - // [mindamage, maxdamage] - // maximum of this! - if(maxdamage >= maxtime * maxdps) - { - totaltime = maxtime; - totaldamage = maxtime * maxdps; - - // this branch increases totaldamage if either t > mintime, or dps > mindps - } - else - { - // maxdamage is inside the interval! - // first, try to use mindps; only if this fails, increase dps as needed - totaltime = min(maxdamage / mindps, maxtime); // maxdamage / mindps >= mindamage / mindps = mintime - totaldamage = maxdamage; - // can totaldamage / totaltime be >= maxdps? - // max(mindps, maxdamage / maxtime) >= maxdps? - // we know maxdamage < maxtime * maxdps - // so it cannot be - - // this branch ALWAYS increases totaldamage, but requires maxdamage < maxtime * maxdps - } + // damage we want to get + maxdamage = mindamage + d; - // total conditions for increasing: - // maxtime > mintime OR maxdps > mindps OR maxtime * maxdps > maxdamage - // however: - // if maxtime = mintime, maxdps = mindps - // then: - // maxdamage = mindamage + d - // mindamage = mindps * mintime = maxdps * maxtime < maxdamage! - // so the last condition is not needed + // but we can't exceed maxtime * maxdps! + totaldamage = min(maxdamage, maxtime * maxdps); + + // LEMMA: + // Look at: + // totaldamage = min(mindamage + d, maxtime * maxdps) + // We see: + // totaldamage <= maxtime * maxdps + // ==> totaldamage / maxdps <= maxtime. + // We also see: + // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps) + // >= min(mintime, maxtime) + // ==> totaldamage / maxdps >= mintime. + + /* + // how long do we damage then? + // at least as long as before + // but, never exceed maxdps + totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma + */ + + // alternate: + // at most as long as maximum allowed + // but, never below mindps + totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma + + // assuming t > mintime, dps > mindps: + // we get d = t * dps = maxtime * maxdps + // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps + // totaldamage / maxdps = maxtime + // totaldamage / mindps > totaldamage / maxdps = maxtime + // FROM THIS: + // a) totaltime = max(mintime, maxtime) = maxtime + // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime + + // assuming t <= mintime: + // we get maxtime = mintime + // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime + // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime + + // assuming dps <= mindps: + // we get mindps = maxdps. + // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime. + // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps + // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps e.fire_damagepersec = totaldamage / totaltime; e.fire_endtime = time + totaltime; diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index 370f2fb987..8867d0725b 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -213,6 +213,7 @@ void GrapplingHookThink() self.aiment.flags &~= FL_ONGROUND; self.aiment.pusher = self.realowner; self.aiment.pushltime = time + autocvar_g_maxpushtime; + self.aiment.istypefrag = self.aiment.BUTTON_CHAT; } } @@ -285,6 +286,7 @@ void GrapplingHook_Damage (entity inflictor, entity attacker, float damage, floa { self.realowner.pusher = attacker; self.realowner.pushltime = time + autocvar_g_maxpushtime; + self.realowner.istypefrag = self.realowner.BUTTON_CHAT; } RemoveGrapplingHook(self.realowner); } diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc index e119e5aba4..b07cae2714 100644 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@ -178,24 +178,29 @@ void SUB_CalcMove_controller_think (void) float phasepos; float nexttick; vector delta; + vector delta2; vector veloc; vector nextpos; + delta = self.destvec; + delta2 = self.destvec2; if(time < self.animstate_endtime) { - delta = self.destvec; nexttick = time + sys_frametime; - if(nexttick < self.animstate_endtime) { - traveltime = self.animstate_endtime - self.animstate_starttime; - phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1] + traveltime = self.animstate_endtime - self.animstate_starttime; + phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1] + if(self.platmovetype != 1) + { phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi] phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1] phasepos = phasepos + 1; // correct range to [0, 2] phasepos = phasepos / 2; // correct range to [0, 1] - nextpos = self.origin + (delta * phasepos); + } + nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos); + // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning) + if(nexttick < self.animstate_endtime) { veloc = nextpos - self.owner.origin; veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame - } else { veloc = self.finaldest - self.owner.origin; veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame @@ -203,6 +208,7 @@ void SUB_CalcMove_controller_think (void) self.owner.velocity = veloc; self.nextthink = nexttick; } else { + // derivative: delta + 2 * delta2 (e.g. for angle positioning) oldself = self; self.owner.think = self.think1; self = self.owner; @@ -211,9 +217,35 @@ void SUB_CalcMove_controller_think (void) } } -void SUB_CalcMove (vector tdest, float tspeed, void() func) +void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest) +{ + // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t + // 2 * control * t - 2 * control * t * t + dest * t * t + // 2 * control * t + (dest - 2 * control) * t * t + + controller.origin = org; // starting point + control -= org; + dest -= org; + + controller.destvec = 2 * control; // control point + controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point +} + +void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest) +{ + // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t + // 2 * control * t - 2 * control * t * t + dest * t * t + // 2 * control * t + (dest - 2 * control) * t * t + + controller.origin = org; // starting point + dest -= org; + + controller.destvec = dest; // end point + controller.destvec2 = '0 0 0'; +} + +void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() func) { - vector delta; float traveltime; entity controller; @@ -224,40 +256,24 @@ void SUB_CalcMove (vector tdest, float tspeed, void() func) self.finaldest = tdest; self.think = SUB_CalcMoveDone; - if (tdest == self.origin) - { - self.velocity = '0 0 0'; - self.nextthink = self.ltime + 0.1; - return; - } - - delta = tdest - self.origin; - traveltime = vlen (delta) / tspeed; + if(tspeed > 0) // positive: start speed + traveltime = 2 * vlen(tcontrol - self.origin) / tspeed; + else // negative: end speed + traveltime = 2 * vlen(tcontrol - tdest) / -tspeed; - if (traveltime < 0.1) + if (traveltime < 0.1) // useless anim { self.velocity = '0 0 0'; self.nextthink = self.ltime + 0.1; return; } - // Very short animations don't really show off the effect - // of controlled animation, so let's just use linear movement. - // Alternatively entities can choose to specify non-controlled movement. - // The only currently implemented alternative movement is linear (value 1) - if (traveltime < 0.15 || self.platmovetype == 1) - { - self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division - self.nextthink = self.ltime + traveltime; - return; - } - controller = spawn(); controller.classname = "SUB_CalcMove_controller"; controller.owner = self; - controller.origin = self.origin; // starting point + controller.platmovetype = self.platmovetype; + SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest); controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit. - controller.destvec = delta; controller.animstate_starttime = time; controller.animstate_endtime = time + traveltime; controller.think = SUB_CalcMove_controller_think; @@ -273,6 +289,43 @@ void SUB_CalcMove (vector tdest, float tspeed, void() func) self = self.owner; } +void SUB_CalcMove (vector tdest, float tspeed, void() func) +{ + vector delta; + float traveltime; + + if (!tspeed) + objerror ("No speed is defined!"); + + self.think1 = func; + self.finaldest = tdest; + self.think = SUB_CalcMoveDone; + + if (tdest == self.origin) + { + self.velocity = '0 0 0'; + self.nextthink = self.ltime + 0.1; + return; + } + + delta = tdest - self.origin; + traveltime = vlen (delta) / tspeed; + + // Very short animations don't really show off the effect + // of controlled animation, so let's just use linear movement. + // Alternatively entities can choose to specify non-controlled movement. + // The only currently implemented alternative movement is linear (value 1) + if (traveltime < 0.15 || self.platmovetype == 1) + { + self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division + self.nextthink = self.ltime + traveltime; + return; + } + + // now just run like a bezier curve... + SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeed, func); +} + void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func) { entity oldself; @@ -382,11 +435,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, lag = 0; // only antilag for clients // change shooter to SOLID_BBOX so the shot can hit corpses + oldsolid = source.dphitcontentsmask; if(source) - { - oldsolid = source.dphitcontentsmask; source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - } if (lag) { @@ -447,10 +498,11 @@ void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE); } -float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking +float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity) // returns the number of traces done, for benchmarking { vector pos, dir, t; float nudge; + entity stopentity; //nudge = 2 * cvar("collision_impactnudge"); // why not? nudge = 0.5; @@ -483,6 +535,8 @@ float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomon dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n"); } + stopentity = trace_ent; + if(trace_startsolid) { // we started inside solid. @@ -495,6 +549,15 @@ float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomon // t is still inside solid? bad // force advance, then, and retry pos = t + dir * nudge; + + // but if we hit an entity, stop RIGHT before it + if(stopatentity && stopentity) + { + trace_ent = stopentity; + trace_endpos = t; + trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); + return c; + } } else { @@ -513,59 +576,9 @@ float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomon } } -void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent) +void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity) { -#if 0 - vector pos, dir, t; - float nudge; - - //nudge = 2 * cvar("collision_impactnudge"); // why not? - nudge = 0.5; - - dir = normalize(v2 - v1); - - pos = v1 + dir * nudge; - - for(;;) - { - if((pos - v1) * dir >= (v2 - v1) * dir) - { - // went too far - trace_fraction = 1; - return; - } - - traceline(pos, v2, nomonsters, forent); - - if(trace_startsolid) - { - // we started inside solid. - // then trace from endpos to pos - t = trace_endpos; - traceline(t, pos, nomonsters, forent); - if(trace_startsolid) - { - // t is inside solid? bad - // force advance, then - pos = pos + dir * nudge; - } - else - { - // we actually LEFT solid! - trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); - return; - } - } - else - { - // pos is outside solid?!? but why?!? never mind, just return it. - trace_endpos = pos; - trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); - return; - } - } -#else - tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent); + tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity); } /* @@ -786,7 +799,15 @@ void SetBrushEntityModel() if(self.model != "") { precache_model(self.model); - setmodel(self, self.model); // no precision needed + if(self.mins != '0 0 0' || self.maxs != '0 0 0') + { + vector mi = self.mins; + vector ma = self.maxs; + setmodel(self, self.model); // no precision needed + setsize(self, mi, ma); + } + else + setmodel(self, self.model); // no precision needed InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET); } setorigin(self, self.origin); @@ -798,7 +819,15 @@ void SetBrushEntityModelNoLOD() if(self.model != "") { precache_model(self.model); - setmodel(self, self.model); // no precision needed + if(self.mins != '0 0 0' || self.maxs != '0 0 0') + { + vector mi = self.mins; + vector ma = self.maxs; + setmodel(self, self.model); // no precision needed + setsize(self, mi, ma); + } + else + setmodel(self, self.model); // no precision needed } setorigin(self, self.origin); ApplyMinMaxScaleAngles(self); diff --git a/qcsrc/server/g_triggers.qc b/qcsrc/server/g_triggers.qc index 4e97136ce8..20ddc65517 100644 --- a/qcsrc/server/g_triggers.qc +++ b/qcsrc/server/g_triggers.qc @@ -466,16 +466,19 @@ void trigger_hurt_touch() Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); } } + else if(other.damagedbytriggers) + { + if(other.takedamage) + { + EXACTTRIGGER_TOUCH; + Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + } else { if (!other.owner) { - if (other.items & IT_KEY1 || other.items & IT_KEY2) // reset flag - { - EXACTTRIGGER_TOUCH; - other.pain_finished = min(other.pain_finished, time + 2); - } - else if (other.classname == "rune") // reset runes + if (other.classname == "rune") // reset runes { EXACTTRIGGER_TOUCH; other.nextthink = min(other.nextthink, time + 1); @@ -545,6 +548,7 @@ void trigger_heal_touch() if (other.iscreature) { if (other.takedamage) + if (!other.deadflag) if (other.triggerhealtime < time) { EXACTTRIGGER_TOUCH; @@ -1390,6 +1394,8 @@ void trigger_impulse_touch1() return; } + str = min(self.radius, vlen(self.origin - other.origin)); + if(self.falloff == 1) str = (str / self.radius) * self.strength; else if(self.falloff == 2) diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index f8697ddcf8..69bf57d51b 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -1,3 +1,7 @@ +#define LATENCY_THINKRATE 10 +.float latency_sum; +.float latency_cnt; +.float latency_time; entity pingplreport; void PingPLReport_Think() { @@ -18,6 +22,15 @@ void PingPLReport_Think() WriteShort(MSG_BROADCAST, max(1, e.ping)); WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255)); WriteByte(MSG_BROADCAST, ceil(e.ping_movementloss * 255)); + + // record latency times for clients throughout the match so we can report it to playerstats + if(time > (e.latency_time + LATENCY_THINKRATE)) + { + e.latency_sum += e.ping; + e.latency_cnt += 1; + e.latency_time = time; + //print("sum: ", ftos(e.latency_sum), ", cnt: ", ftos(e.latency_cnt), ", avg: ", ftos(e.latency_sum / e.latency_cnt), ".\n"); + } } else { @@ -295,9 +308,8 @@ void cvar_changes_init() BADCVAR("g_balance_kill_delay"); BADCVAR("g_ca_point_leadlimit"); BADCVAR("g_ctf_captimerecord_always"); - BADCVAR("g_ctf_flag_capture_effects"); BADCVAR("g_ctf_flag_glowtrails"); - BADCVAR("g_ctf_flag_pickup_effects"); + BADCVAR("g_ctf_flag_pickup_verbosename"); BADCVAR("g_domination_point_leadlimit"); BADCVAR("g_forced_respawn"); BADCVAR("g_keyhunt_point_leadlimit"); @@ -348,7 +360,8 @@ void cvar_changes_init() BADCVAR("gametype"); BADCVAR("g_antilag"); BADCVAR("g_balance_teams"); - BADCVAR("g_balance_teams_force"); + BADCVAR("g_balance_teams_prevent_imbalance"); + BADCVAR("g_balance_teams_scorefactor"); BADCVAR("g_ban_sync_trusted_servers"); BADCVAR("g_ban_sync_uri"); BADCVAR("g_ctf_ignore_frags"); @@ -368,7 +381,6 @@ void cvar_changes_init() BADCVAR("g_maplist_votable_nodetail"); BADCVAR("g_maplist_votable_suggestions"); BADCVAR("g_maxplayers"); - BADCVAR("g_minstagib"); BADCVAR("g_mirrordamage"); BADCVAR("g_nexball_goallimit"); BADCVAR("g_powerups"); @@ -415,6 +427,11 @@ void cvar_changes_init() BADPREFIX("g_warmup_"); BADPREFIX("sv_ready_restart_"); + // mutators that announce themselves properly to the server browser + BADCVAR("g_minstagib"); + BADCVAR("g_new_toys"); + BADCVAR("g_nix"); + if(autocvar_g_minstagib) { BADCVAR("g_grappling_hook"); @@ -534,8 +551,8 @@ void spawnfunc___init_dedicated_server(void) self.classname = "worldspawn"; // safeguard against various stuff ;) // needs to be done so early because of the constants they create - RegisterWeapons(); - RegisterGametypes(); + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); MapInfo_Enumerate(); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); @@ -543,7 +560,6 @@ void spawnfunc___init_dedicated_server(void) void Map_MarkAsRecent(string m); float world_already_spawned; -void RegisterWeapons(); void Nagger_Init(); void ClientInit_Spawn(); void WeaponStats_Init(); @@ -581,8 +597,8 @@ void spawnfunc_worldspawn (void) } // needs to be done so early because of the constants they create - RegisterWeapons(); - RegisterGametypes(); + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid)); @@ -634,6 +650,8 @@ void spawnfunc_worldspawn (void) Map_MarkAsRecent(mapname); + PlayerStats_Init(); // we need this to be initiated before InitGameplayMode + precache_model ("null"); // we need this one before InitGameplayMode InitGameplayMode(); readlevelcvars(); @@ -764,7 +782,7 @@ void spawnfunc_worldspawn (void) WeaponStats_Init(); - addstat(STAT_WEAPONS, AS_INT, weapons); + WEPSET_ADDSTAT(); addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon); addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon); addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime); @@ -812,6 +830,9 @@ void spawnfunc_worldspawn (void) // secrets addstat(STAT_SECRETS_TOTAL, AS_FLOAT, stat_secrets_total); addstat(STAT_SECRETS_FOUND, AS_FLOAT, stat_secrets_found); + + // misc + addstat(STAT_RESPAWN_TIME, AS_FLOAT, stat_respawn_time); next_pingtime = time + 5; @@ -907,8 +928,6 @@ void spawnfunc_worldspawn (void) cvar_set("sv_curl_serverpackages", substring(s, 1, -1)); } - PlayerStats_Init(); - // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes modname = "Xonotic"; // physics/balance/config changes that count as mod @@ -1437,6 +1456,8 @@ void DumpStats(float final) print(s, "\n"); if(to_eventlog) GameLogEcho(s); + + file = -1; if(to_file) { file = fopen(autocvar_sv_logscores_filename, FILE_APPEND); @@ -1973,6 +1994,9 @@ float WinningCondition_Scores(float limit, float leadlimit) limitreached = (limitreached || leadlimitreached); } + if(limit) + game_completion_ratio = max(game_completion_ratio, bound(0, WinningConditionHelper_topscore / limit, 1)); + return GetWinningCode( WinningConditionHelper_topscore && limitreached, WinningConditionHelper_equality @@ -2068,10 +2092,14 @@ float WinningCondition_RanOutOfSpawns() else if(team1_score + team2_score + team3_score + team4_score == 1) { float t, i; - if(team1_score) t = COLOR_TEAM1; - if(team2_score) t = COLOR_TEAM2; - if(team3_score) t = COLOR_TEAM3; - if(team4_score) t = COLOR_TEAM4; + if(team1_score) + t = COLOR_TEAM1; + else if(team2_score) + t = COLOR_TEAM2; + else if(team3_score) + t = COLOR_TEAM3; + else // if(team4_score) + t = COLOR_TEAM4; CheckAllowedTeams(world); for(i = 0; i < MAX_TEAMSCORE; ++i) { @@ -2157,6 +2185,11 @@ void CheckRules_World() float wantovertime; wantovertime = 0; + if(timelimit > game_starttime) + game_completion_ratio = (time - game_starttime) / (timelimit - game_starttime); + else + game_completion_ratio = 0; + if(checkrules_suddendeathend) { if(!checkrules_suddendeathwarning) @@ -2359,6 +2392,7 @@ void MapVote_AddVotable(string nextMap, float isSuggestion) mapvote_maps[mapvote_count] = strzone(nextMap); mapvote_maps_suggested[mapvote_count] = isSuggestion; + pakfile = string_null; for(i = 0; i < mapvote_screenshot_dirs_count; ++i) { mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]); diff --git a/qcsrc/server/ipban.qc b/qcsrc/server/ipban.qc index fc889ecf2d..38a1e249d7 100644 --- a/qcsrc/server/ipban.qc +++ b/qcsrc/server/ipban.qc @@ -324,6 +324,7 @@ void Ban_View() print("^2Listing all existing active bans:\n"); + n = 0; for(i = 0; i < ban_count; ++i) { if(time > ban_expire[i]) @@ -448,6 +449,15 @@ float Ban_MaybeEnforceBan(entity client) return FALSE; } +.float ban_checked; +float Ban_MaybeEnforceBanOnce(entity client) +{ + if(client.ban_checked) + return FALSE; + client.ban_checked = TRUE; + return Ban_MaybeEnforceBan(self); +} + string Ban_Enforce(float i, string reason) { string s; @@ -466,7 +476,7 @@ string Ban_Enforce(float i, string reason) reason = strcat(reason, ", "); reason = strcat(reason, e.netname); } - s = strcat(s, "^1NOTE:^7 banned client ", e.netname, "^7 has to go\n"); + s = strcat(s, "^1NOTE:^7 banned client ", e.netaddress, "^7 has to go\n"); dropclient(e); } bprint(s); diff --git a/qcsrc/server/ipban.qh b/qcsrc/server/ipban.qh index 15939464e0..d466942e31 100644 --- a/qcsrc/server/ipban.qh +++ b/qcsrc/server/ipban.qh @@ -1,6 +1,7 @@ void Ban_SaveBans(); void Ban_LoadBans(); float Ban_MaybeEnforceBan(entity client); +float Ban_MaybeEnforceBanOnce(entity client); float BanCommand(string command); void OnlineBanList_URI_Get_Callback(float id, float status, string data); diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc index e39f281345..7b386ec975 100644 --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@ -36,6 +36,8 @@ string item_keys_keylist(float keylist) { if ((keylist & (keylist-1)) != 0) return strcat("the ", item_keys_names[lowestbit(keylist)]); + n = ""; + base = 0; while (keylist) { l = lowestbit(keylist); if (n) @@ -181,12 +183,13 @@ void spawnfunc_item_key() { break; default: + _netname = "FLUFFY PINK keycard"; + _colormod = '1 1 1'; + if (!self.netname) { objerror("item_key doesn't have a default name for this key and a custom one was not specified!"); remove(self); return; - } else if (!self.colormod) { - _colormod = '1 1 1'; } break; diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 7065542b19..20828800de 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -67,6 +67,16 @@ float DistributeEvenly_Get(float weight) DistributeEvenly_amount -= f; return f; } +float DistributeEvenly_GetRandomized(float weight) +{ + float f; + if (weight <= 0) + return 0; + f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight); + DistributeEvenly_totalweight -= weight; + DistributeEvenly_amount -= f; + return f; +} #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e) @@ -85,9 +95,12 @@ string STR_OBSERVER = "observer"; #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT) #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL) #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER) +#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if(v.classname != STR_PLAYER) #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER) #endif +#define CENTER_OR_VIEWOFS(ent) (ent.origin + ((ent.classname == STR_PLAYER) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) + // copies a string to a tempstring (so one can strunzone it) string strcat1(string s) = #115; // FRIK_FILE @@ -526,7 +539,7 @@ string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo) } void GetCvars(float f) { - string s; + string s = string_null; if (f > 0) s = strcat1(argv(f)); @@ -740,6 +753,7 @@ float g_pickup_healthmega_anyway; float g_pickup_ammo_anyway; float g_pickup_weapons_anyway; float g_weaponarena; +WEPSET_DECLARE_A(g_weaponarena_weapons); float g_weaponarena_random; float g_weaponarena_random_with_laser; string g_weaponarena_list; @@ -749,7 +763,9 @@ float g_weapondamagefactor; float g_weaponforcefactor; float g_weaponspreadfactor; -float start_weapons; +WEPSET_DECLARE_A(start_weapons); +WEPSET_DECLARE_A(start_weapons_default); +WEPSET_DECLARE_A(start_weapons_defaultmask); float start_items; float start_ammo_shells; float start_ammo_nails; @@ -758,7 +774,9 @@ float start_ammo_cells; float start_ammo_fuel; float start_health; float start_armorvalue; -float warmup_start_weapons; +WEPSET_DECLARE_A(warmup_start_weapons); +WEPSET_DECLARE_A(warmup_start_weapons_default); +WEPSET_DECLARE_A(warmup_start_weapons_defaultmask); float warmup_start_ammo_shells; float warmup_start_ammo_nails; float warmup_start_ammo_rockets; @@ -767,34 +785,48 @@ float warmup_start_ammo_fuel; float warmup_start_health; float warmup_start_armorvalue; float g_weapon_stay; -float g_ghost_items; entity get_weaponinfo(float w); float want_weapon(string cvarprefix, entity weaponinfo, float allguns) { var float i = weaponinfo.weapon; + var float d = 0; if (!i) return 0; - var float t = cvar(strcat(cvarprefix, weaponinfo.netname)); - - if (t < 0) // "default" weapon selection + if (g_lms || g_ca || allguns) { - if (g_lms || g_ca || allguns) - t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL); - else if(t < -1) - t = 0; - else if (g_cts) - t = (i == WEP_SHOTGUN); - else if (g_nexball) - t = 0; // weapon is set a few lines later + if(weaponinfo.spawnflags & WEP_FLAG_NORMAL) + d = TRUE; else - t = (i == WEP_LASER || i == WEP_SHOTGUN); - if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook - t |= (i == WEP_HOOK); + d = FALSE; } + else if (g_cts) + d = (i == WEP_SHOTGUN); + else if (g_nexball) + d = 0; // weapon is set a few lines later + else + d = (i == WEP_LASER || i == WEP_SHOTGUN); + + if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook + d |= (i == WEP_HOOK); + if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns + d = 0; + + var float t = cvar(strcat(cvarprefix, weaponinfo.netname)); + + //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n")); + + // bit order in t: + // 1: want or not + // 2: is default? + // 4: is set by default? + if(t < 0) + t = 4 | (3 * d); + else + t |= (2 * d); return t; } @@ -806,7 +838,9 @@ void readplayerstartcvars() string s; // initialize starting values for players - start_weapons = 0; + WEPSET_CLEAR_A(start_weapons); + WEPSET_CLEAR_A(start_weapons_default); + WEPSET_CLEAR_A(start_weapons_defaultmask); start_items = 0; start_ammo_shells = 0; start_ammo_nails = 0; @@ -816,6 +850,8 @@ void readplayerstartcvars() start_armorvalue = cvar("g_balance_armor_start"); g_weaponarena = 0; + WEPSET_CLEAR_A(g_weaponarena_weapons); + s = cvar_string("g_weaponarena"); if (s == "0" || s == "") { @@ -833,34 +869,35 @@ void readplayerstartcvars() } else if (s == "all") { + g_weaponarena = 1; g_weaponarena_list = "All Weapons"; for (j = WEP_FIRST; j <= WEP_LAST; ++j) { e = get_weaponinfo(j); - g_weaponarena |= e.weapons; - weapon_action(e.weapon, WR_PRECACHE); + if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + WEPSET_OR_AW(g_weaponarena_weapons, j); } } else if (s == "most") { + g_weaponarena = 1; g_weaponarena_list = "Most Weapons"; for (j = WEP_FIRST; j <= WEP_LAST; ++j) { e = get_weaponinfo(j); - if (e.spawnflags & WEP_FLAG_NORMAL) - { - g_weaponarena |= e.weapons; - weapon_action(e.weapon, WR_PRECACHE); - } + if not(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + if (e.spawnflags & WEP_FLAG_NORMAL) + WEPSET_OR_AW(g_weaponarena_weapons, j); } } else if (s == "none") { + g_weaponarena = 1; g_weaponarena_list = "No Weapons"; - g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit! } else { + g_weaponarena = 1; t = tokenize_console(s); g_weaponarena_list = ""; for (i = 0; i < t; ++i) @@ -871,8 +908,7 @@ void readplayerstartcvars() e = get_weaponinfo(j); if (e.netname == s) { - g_weaponarena |= e.weapons; - weapon_action(e.weapon, WR_PRECACHE); + WEPSET_OR_AW(g_weaponarena_weapons, j); g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & "); break; } @@ -896,7 +932,7 @@ void readplayerstartcvars() g_minstagib = 0; // incompatible g_pinata = 0; // incompatible g_weapon_stay = 0; // incompatible - start_weapons = g_weaponarena; + WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons); if(!(g_lms || g_ca)) start_items |= IT_UNLIMITED_AMMO; } @@ -907,8 +943,7 @@ void readplayerstartcvars() g_bloodloss = 0; // incompatible start_health = 100; start_armorvalue = 0; - start_weapons = WEPBIT_MINSTANEX; - weapon_action(WEP_MINSTANEX, WR_PRECACHE); + WEPSET_COPY_AW(start_weapons, WEP_MINSTANEX); g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha"); start_items |= IT_UNLIMITED_SUPERWEAPONS; @@ -920,8 +955,13 @@ void readplayerstartcvars() for (i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - if(want_weapon("g_start_weapon_", e, FALSE)) - start_weapons |= e.weapons; + float w = want_weapon("g_start_weapon_", e, FALSE); + if(w & 1) + WEPSET_OR_AW(start_weapons, i); + if(w & 2) + WEPSET_OR_AW(start_weapons_default, i); + if(w & 4) + WEPSET_OR_AW(start_weapons_defaultmask, i); } } @@ -979,7 +1019,9 @@ void readplayerstartcvars() warmup_start_ammo_fuel = start_ammo_fuel; warmup_start_health = start_health; warmup_start_armorvalue = start_armorvalue; - warmup_start_weapons = start_weapons; + WEPSET_COPY_AA(warmup_start_weapons, start_weapons); + WEPSET_COPY_AA(warmup_start_weapons_default, start_weapons_default); + WEPSET_COPY_AA(warmup_start_weapons_defaultmask, start_weapons_defaultmask); if (!g_weaponarena && !g_minstagib && !g_ca) { @@ -990,17 +1032,29 @@ void readplayerstartcvars() warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel"); warmup_start_health = cvar("g_warmup_start_health"); warmup_start_armorvalue = cvar("g_warmup_start_armor"); - warmup_start_weapons = 0; + WEPSET_CLEAR_A(warmup_start_weapons); + WEPSET_CLEAR_A(warmup_start_weapons_default); + WEPSET_CLEAR_A(warmup_start_weapons_defaultmask); for (i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns"))) - warmup_start_weapons |= e.weapons; + float w = want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")); + if(w & 1) + WEPSET_OR_AW(warmup_start_weapons, i); + if(w & 2) + WEPSET_OR_AW(warmup_start_weapons_default, i); + if(w & 4) + WEPSET_OR_AW(warmup_start_weapons_defaultmask, i); } } } - if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK))) + if (g_jetpack) + start_items |= IT_JETPACK; + + MUTATOR_CALLHOOK(SetStartItems); + + if ((start_items & IT_JETPACK) || (g_grappling_hook && WEPSET_CONTAINS_AW(start_weapons, WEP_HOOK))) { g_grappling_hook = 0; // these two can't coexist, as they use the same button start_items |= IT_FUEL_REGEN; @@ -1008,16 +1062,11 @@ void readplayerstartcvars() warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable")); } - if (g_jetpack) - start_items |= IT_JETPACK; - - MUTATOR_CALLHOOK(SetStartItems); - for (i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - if(e.weapons & (start_weapons | warmup_start_weapons)) - weapon_action(e.weapon, WR_PRECACHE); + if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i)) + weapon_action(i, WR_PRECACHE); } start_ammo_shells = max(0, start_ammo_shells); @@ -1065,19 +1114,28 @@ float sv_pitch_fixyaw; string GetGametype(); // g_world.qc void readlevelcvars(void) { - // first load all the mutators - if(cvar("g_invincible_projectiles")) - MUTATOR_ADD(mutator_invincibleprojectiles); - if(cvar("g_nix")) - MUTATOR_ADD(mutator_nix); + g_minstagib = cvar("g_minstagib"); + + // load ALL the mutators if(cvar("g_dodging")) MUTATOR_ADD(mutator_dodging); - if(cvar("g_rocket_flying")) - MUTATOR_ADD(mutator_rocketflying); - if(cvar("g_vampire")) - MUTATOR_ADD(mutator_vampire); if(cvar("g_spawn_near_teammate")) MUTATOR_ADD(mutator_spawn_near_teammate); + if(!g_minstagib) + { + if(cvar("g_invincible_projectiles")) + MUTATOR_ADD(mutator_invincibleprojectiles); + if(cvar("g_new_toys")) + MUTATOR_ADD(mutator_new_toys); + if(cvar("g_nix")) + MUTATOR_ADD(mutator_nix); + if(cvar("g_rocket_flying")) + MUTATOR_ADD(mutator_rocketflying); + if(cvar("g_vampire")) + MUTATOR_ADD(mutator_vampire); + if(cvar("g_superspectate")) + MUTATOR_ADD(mutator_superspec); + } // is this a mutator? is this a mode? if(cvar("g_sandbox")) @@ -1123,12 +1181,10 @@ void readlevelcvars(void) g_grappling_hook = cvar("g_grappling_hook"); g_jetpack = cvar("g_jetpack"); g_midair = cvar("g_midair"); - g_minstagib = cvar("g_minstagib"); g_norecoil = cvar("g_norecoil"); 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"); @@ -1206,11 +1262,6 @@ void readlevelcvars(void) if(!g_weapon_stay) g_weapon_stay = cvar("g_weapon_stay"); - g_ghost_items = cvar("g_ghost_items"); - - if(g_ghost_items >= 1) - g_ghost_items = 0.25; // default alpha value - if not(inWarmupStage && !g_ca) game_starttime = cvar("g_start_delay"); @@ -1656,7 +1707,7 @@ void make_safe_for_remove(entity e) { if (e.initialize_entity) { - entity ent, prev; + entity ent, prev = world; for (ent = initialize_entity_first; ent; ) { if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e))) @@ -1728,6 +1779,7 @@ void InitializeEntity(entity e, void(void) func, float order) e.initialize_entity_order = order; cur = initialize_entity_first; + prev = world; for (;;) { if (!cur || cur.initialize_entity_order > order) @@ -1986,6 +2038,9 @@ float WarpZone_Projectile_Touch_ImpactFilter_Callback() } #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return +#define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) +#define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP)) + void URI_Get_Callback(float id, float status, string data) { if(url_URI_Get_Callback(id, status, data)) @@ -2075,7 +2130,7 @@ void race_writeTime(string map, float t, string myuid) float newpos; newpos = race_readPos(map, t); - float i, prevpos; + float i, prevpos = 0; for(i = 1; i <= RANKINGS_CNT; ++i) { if(race_readUID(map, i) == myuid) @@ -2137,6 +2192,8 @@ float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, f org = world.mins; delta = world.maxs - world.mins; + start = end = org; + for (i = 0; i < attempts; ++i) { start_x = org_x + random() * delta_x; @@ -2393,12 +2450,8 @@ vector shotorg_adjust(vector vecs, float y_is_right, float visual) void attach_sameorigin(entity e, entity to, string tag) { vector org, t_forward, t_left, t_up, e_forward, e_up; - vector org0, ang0; float tagscale; - ang0 = e.angles; - org0 = e.origin; - org = e.origin - gettaginfo(to, gettagindex(to, tag)); tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag t_forward = v_forward * tagscale; @@ -2746,6 +2799,8 @@ float isPushable(entity e) { if(e.iscreature) return TRUE; + if(e.pushable) + return TRUE; switch(e.classname) { case "body": diff --git a/qcsrc/server/mode_onslaught.qc b/qcsrc/server/mode_onslaught.qc deleted file mode 100644 index 2e0443c8c1..0000000000 --- a/qcsrc/server/mode_onslaught.qc +++ /dev/null @@ -1,1430 +0,0 @@ -void onslaught_generator_updatesprite(entity e); -void onslaught_controlpoint_updatesprite(entity e); -void onslaught_link_checkupdate(); - -.entity sprite; -.string target2; -.float iscaptured; -.float islinked; -.float isgenneighbor_red; -.float isgenneighbor_blue; -.float iscpneighbor_red; -.float iscpneighbor_blue; -.float isshielded; -.float lasthealth; -.float lastteam; -.float lastshielded; -.float lastcaptured; - -.string model1, model2, model3; - -void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - self.velocity = self.velocity + vforce; -} - -.float giblifetime; -void ons_throwgib_think() -{ - float d; - - self.nextthink = time + 0.05; - - d = self.giblifetime - time; - - if(d<0) - { - self.think = SUB_Remove; - return; - } - if(d<1) - self.alpha = d; - - if(d>2) - if(random()<0.6) - pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1); -} - -void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn) -{ - entity gib; - - gib = spawn(); - - setmodel(gib, smodel); - setorigin(gib, v_from); - gib.solid = SOLID_BBOX; - gib.movetype = MOVETYPE_BOUNCE; - gib.takedamage = DAMAGE_YES; - gib.event_damage = ons_gib_damage; - gib.health = -1; - gib.effects = EF_LOWPRECISION; - gib.flags = FL_NOTARGET; - gib.velocity = v_to; - gib.giblifetime = time + f_lifetime; - - if (b_burn) - { - gib.think = ons_throwgib_think; - gib.nextthink = time + 0.05; - } - else - SUB_SetFade(gib, gib.giblifetime, 2); -} - -void onslaught_updatelinks() -{ - entity l, links; - float stop, t1, t2, t3, t4; - // first check if the game has ended - dprint("--- updatelinks ---\n"); - links = findchain(classname, "onslaught_link"); - // mark generators as being shielded and networked - l = findchain(classname, "onslaught_generator"); - while (l) - { - if (l.iscaptured) - dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"); - else - dprint(etos(l), " (generator) is destroyed\n"); - l.islinked = l.iscaptured; - l.isshielded = l.iscaptured; - l = l.chain; - } - // mark points as shielded and not networked - l = findchain(classname, "onslaught_controlpoint"); - while (l) - { - l.islinked = FALSE; - l.isshielded = TRUE; - l.isgenneighbor_red = FALSE; - l.isgenneighbor_blue = FALSE; - l.iscpneighbor_red = FALSE; - l.iscpneighbor_blue = FALSE; - dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n"); - l = l.chain; - } - // flow power outward from the generators through the network - l = links; - while (l) - { - dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n"); - l = l.chain; - } - stop = FALSE; - while (!stop) - { - stop = TRUE; - l = links; - while (l) - { - // if both points are captured by the same team, and only one of - // them is powered, mark the other one as powered as well - if (l.enemy.iscaptured && l.goalentity.iscaptured) - if (l.enemy.islinked != l.goalentity.islinked) - if (l.enemy.team == l.goalentity.team) - { - if (!l.goalentity.islinked) - { - stop = FALSE; - l.goalentity.islinked = TRUE; - dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"); - } - else if (!l.enemy.islinked) - { - stop = FALSE; - l.enemy.islinked = TRUE; - dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"); - } - } - l = l.chain; - } - } - // now that we know which points are powered we can mark their neighbors - // as unshielded if team differs - l = links; - while (l) - { - if (l.goalentity.islinked) - { - if (l.goalentity.team != l.enemy.team) - { - dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"); - l.enemy.isshielded = FALSE; - } - if(l.goalentity.classname == "onslaught_generator") - { - if(l.goalentity.team == COLOR_TEAM1) - l.enemy.isgenneighbor_red = TRUE; - else if(l.goalentity.team == COLOR_TEAM2) - l.enemy.isgenneighbor_blue = TRUE; - } - else - { - if(l.goalentity.team == COLOR_TEAM1) - l.enemy.iscpneighbor_red = TRUE; - else if(l.goalentity.team == COLOR_TEAM2) - l.enemy.iscpneighbor_blue = TRUE; - } - } - if (l.enemy.islinked) - { - if (l.goalentity.team != l.enemy.team) - { - dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"); - l.goalentity.isshielded = FALSE; - } - if(l.enemy.classname == "onslaught_generator") - { - if(l.enemy.team == COLOR_TEAM1) - l.goalentity.isgenneighbor_red = TRUE; - else if(l.enemy.team == COLOR_TEAM2) - l.goalentity.isgenneighbor_blue = TRUE; - } - else - { - if(l.enemy.team == COLOR_TEAM1) - l.goalentity.iscpneighbor_red = TRUE; - else if(l.enemy.team == COLOR_TEAM2) - l.goalentity.iscpneighbor_blue = TRUE; - } - } - l = l.chain; - } - // now update the takedamage and alpha variables on generator shields - l = findchain(classname, "onslaught_generator"); - while (l) - { - if (l.isshielded) - { - dprint(etos(l), " (generator) is shielded\n"); - l.enemy.alpha = 1; - l.takedamage = DAMAGE_NO; - l.bot_attack = FALSE; - } - else - { - dprint(etos(l), " (generator) is not shielded\n"); - l.enemy.alpha = -1; - l.takedamage = DAMAGE_AIM; - l.bot_attack = TRUE; - } - l = l.chain; - } - // now update the takedamage and alpha variables on control point icons - l = findchain(classname, "onslaught_controlpoint"); - while (l) - { - if (l.isshielded) - { - dprint(etos(l), " (point) is shielded\n"); - l.enemy.alpha = 1; - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_NO; - l.goalentity.bot_attack = FALSE; - } - } - else - { - dprint(etos(l), " (point) is not shielded\n"); - l.enemy.alpha = -1; - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_AIM; - l.goalentity.bot_attack = TRUE; - } - } - onslaught_controlpoint_updatesprite(l); - l = l.chain; - } - // count generators owned by each team - t1 = t2 = t3 = t4 = 0; - l = findchain(classname, "onslaught_generator"); - while (l) - { - if (l.iscaptured) - { - if (l.team == COLOR_TEAM1) t1 = 1; - if (l.team == COLOR_TEAM2) t2 = 1; - if (l.team == COLOR_TEAM3) t3 = 1; - if (l.team == COLOR_TEAM4) t4 = 1; - } - onslaught_generator_updatesprite(l); - l = l.chain; - } - // see if multiple teams remain (if not, it's game over) - if (t1 + t2 + t3 + t4 < 2) - dprint("--- game over ---\n"); - else - dprint("--- done updating links ---\n"); -} - -float onslaught_controlpoint_can_be_linked(entity cp, float t) -{ - if(t == COLOR_TEAM1) - { - if(cp.isgenneighbor_red) - return 2; - if(cp.iscpneighbor_red) - return 1; - } - else if(t == COLOR_TEAM2) - { - if(cp.isgenneighbor_blue) - return 2; - if(cp.iscpneighbor_blue) - return 1; - } - return 0; - /* - entity e; - // check to see if this player has a legitimate claim to capture this - // control point - more specifically that there is a captured path of - // points leading back to the team generator - e = findchain(classname, "onslaught_link"); - while (e) - { - if (e.goalentity == cp) - { - dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)"); - if (e.enemy.islinked) - { - dprint(" which is linked"); - if (e.enemy.team == t) - { - dprint(" and has the correct team!\n"); - return 1; - } - else - dprint(" but has the wrong team\n"); - } - else - dprint("\n"); - } - else if (e.enemy == cp) - { - dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)"); - if (e.goalentity.islinked) - { - dprint(" which is linked"); - if (e.goalentity.team == t) - { - dprint(" and has a team!\n"); - return 1; - } - else - dprint(" but has the wrong team\n"); - } - else - dprint("\n"); - } - e = e.chain; - } - return 0; - */ -} - -float onslaught_controlpoint_attackable(entity cp, float t) - // -2: SAME TEAM, attackable by enemy! - // -1: SAME TEAM! - // 0: off limits - // 1: attack it - // 2: touch it - // 3: attack it (HIGH PRIO) - // 4: touch it (HIGH PRIO) -{ - float a; - - if(cp.isshielded) - { - return 0; - } - else if(cp.goalentity) - { - // if there's already an icon built, nothing happens - if(cp.team == t) - { - a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t); - if(a) // attackable by enemy? - return -2; // EMERGENCY! - return -1; - } - // we know it can be linked, so no need to check - // but... - a = onslaught_controlpoint_can_be_linked(cp, t); - if(a == 2) // near our generator? - return 3; // EMERGENCY! - return 1; - } - else - { - // free point - if(onslaught_controlpoint_can_be_linked(cp, t)) - { - a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t); - if(a == 2) - return 4; // GET THIS ONE NOW! - else - return 2; // TOUCH ME - } - } - return 0; -} - -float overtime_msg_time; -void onslaught_generator_think() -{ - float d; - entity e; - self.nextthink = ceil(time + 1); - if (!gameover) - { - if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) - { - if (!overtime_msg_time) - { - FOR_EACH_PLAYER(e) - centerprint(e, "^3Now playing ^1OVERTIME^3!\n^3Generators start now to self-damaging.\n^3The more control points your team holds,\n^3the more damage the enemy generator gets."); - overtime_msg_time = time; - } - // self.max_health / 300 gives 5 minutes of overtime. - // control points reduce the overtime duration. - sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTN_NORM); - d = 1; - e = findchain(classname, "onslaught_controlpoint"); - while (e) - { - if (e.team != self.team) - if (e.islinked) - d = d + 1; - e = e.chain; - } - if(autocvar_g_campaign && autocvar__campaign_testrun) - d = d * self.max_health; - else - d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath); - Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0'); - } - else if (overtime_msg_time) - overtime_msg_time = 0; - } -} - -void onslaught_generator_ring_spawn(vector org) -{ - modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25); -} - -void onslaught_generator_ray_think() -{ - self.nextthink = time + 0.05; - if(self.count > 10) - { - self.think = SUB_Remove; - return; - } - - if(self.count > 5) - self.alpha -= 0.1; - else - self.alpha += 0.1; - - self.scale += 0.2; - self.count +=1; -} - -void onslaught_generator_ray_spawn(vector org) -{ - entity e; - e = spawn(); - setmodel(e, "models/onslaught/ons_ray.md3"); - setorigin(e, org); - e.angles = randomvec() * 360; - e.alpha = 0; - e.scale = random() * 5 + 8; - e.think = onslaught_generator_ray_think; - e.nextthink = time + 0.05; -} - -void onslaught_generator_shockwave_spawn(vector org) -{ - shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5); -} - -void onslaught_generator_damage_think() -{ - if(self.owner.health < 0) - { - self.think = SUB_Remove; - return; - } - self.nextthink = time+0.1; - - // damaged fx (less probable the more damaged is the generator) - if(random() < 0.9 - self.owner.health / self.owner.max_health) - if(random() < 0.01) - { - pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); - sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM); - } - else - pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); -} - -void onslaught_generator_damage_spawn(entity gd_owner) -{ - entity e; - e = spawn(); - e.owner = gd_owner; - e.health = self.owner.health; - setorigin(e, gd_owner.origin); - e.think = onslaught_generator_damage_think; - e.nextthink = time+1; -} - -void onslaught_generator_deaththink() -{ - vector org; - float i; - - if not (self.count) - self.count = 40; - - // White shockwave - if(self.count==40||self.count==20) - { - onslaught_generator_ring_spawn(self.origin); - sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM); - } - - // Throw some gibs - if(random() < 0.3) - { - i = random(); - if(i < 0.3) - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE); - else if(i > 0.7) - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE); - else - ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE); - } - - // Spawn fire balls - for(i=0;i < 10;++i) - { - org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); - pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1); - } - - // Short explosion sound + small explosion - if(random() < 0.25) - { - te_explosion(self.origin); - sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); - } - - // Particles - org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); - pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1); - - // rays - if(random() > 0.25 ) - { - onslaught_generator_ray_spawn(self.origin); - } - - // Final explosion - if(self.count==1) - { - org = self.origin; - te_explosion(org); - onslaught_generator_shockwave_spawn(org); - pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1); - sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - } - else - self.nextthink = time + 0.05; - - self.count = self.count - 1; -} - -void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - float i; - if (damage <= 0) - return; - if(inWarmupStage) - return; - if (attacker != self) - { - if (self.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (attacker.classname == "player") - { - play2(attacker, "onslaught/damageblockedbyshield.wav"); - self.pain_finished = time + 1; - } - return; - } - if (time > self.pain_finished) - { - self.pain_finished = time + 10; - bprint(ColoredTeamName(self.team), " generator under attack!\n"); - play2team(self.team, "onslaught/generator_underattack.wav"); - } - } - self.health = self.health - damage; - WaypointSprite_UpdateHealth(self.sprite, self.health); - // choose an animation frame based on health - self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); - // see if the generator is still functional, or dying - if (self.health > 0) - { -#ifdef ONSLAUGHT_SPAM - float h, lh; - lh = ceil(self.lasthealth / 100) * 100; - h = ceil(self.health / 100) * 100; - if(lh != h) - bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n"); -#endif - self.lasthealth = self.health; - } - else if not(inWarmupStage) - { - if (attacker == self) - bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n"); - else - { - string t; - t = ColoredTeamName(attacker.team); - bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n"); - } - self.iscaptured = FALSE; - self.islinked = FALSE; - self.isshielded = FALSE; - self.takedamage = DAMAGE_NO; // can't be hurt anymore - self.event_damage = SUB_Null; // won't do anything if hurt - self.count = 0; // reset counter - self.think = onslaught_generator_deaththink; // explosion sequence - self.nextthink = time; // start exploding immediately - self.think(); // do the first explosion now - - WaypointSprite_UpdateMaxHealth(self.sprite, 0); - - onslaught_updatelinks(); - } - - if(self.health <= 0) - setmodel(self, "models/onslaught/generator_dead.md3"); - else if(self.health < self.max_health * 0.10) - setmodel(self, "models/onslaught/generator_dmg9.md3"); - else if(self.health < self.max_health * 0.20) - setmodel(self, "models/onslaught/generator_dmg8.md3"); - else if(self.health < self.max_health * 0.30) - setmodel(self, "models/onslaught/generator_dmg7.md3"); - else if(self.health < self.max_health * 0.40) - setmodel(self, "models/onslaught/generator_dmg6.md3"); - else if(self.health < self.max_health * 0.50) - setmodel(self, "models/onslaught/generator_dmg5.md3"); - else if(self.health < self.max_health * 0.60) - setmodel(self, "models/onslaught/generator_dmg4.md3"); - else if(self.health < self.max_health * 0.70) - setmodel(self, "models/onslaught/generator_dmg3.md3"); - else if(self.health < self.max_health * 0.80) - setmodel(self, "models/onslaught/generator_dmg2.md3"); - else if(self.health < self.max_health * 0.90) - setmodel(self, "models/onslaught/generator_dmg1.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - - // Throw some flaming gibs on damage, more damage = more chance for gib - if(random() < damage/220) - { - sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - i = random(); - if(i < 0.3) - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE); - else if(i > 0.7) - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE); - else - ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE); - } - else - { - // particles on every hit - pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1); - - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM); - else - sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM); - } - - //throw some gibs on damage - if(random() < damage/200+0.2) - if(random() < 0.5) - ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE); -} - -// update links after a delay -void onslaught_generator_delayed() -{ - onslaught_updatelinks(); - // now begin normal thinking - self.think = onslaught_generator_think; - self.nextthink = time; -} - -string onslaught_generator_waypointsprite_for_team(entity e, float t) -{ - if(t == e.team) - { - if(e.team == COLOR_TEAM1) - return "ons-gen-red"; - else if(e.team == COLOR_TEAM2) - return "ons-gen-blue"; - } - if(e.isshielded) - return "ons-gen-shielded"; - if(e.team == COLOR_TEAM1) - return "ons-gen-red"; - else if(e.team == COLOR_TEAM2) - return "ons-gen-blue"; - return ""; -} - -void onslaught_generator_updatesprite(entity e) -{ - string s1, s2, s3; - s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1); - s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2); - s3 = onslaught_generator_waypointsprite_for_team(e, -1); - WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3); - - if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) - { - e.lastteam = e.team + 2; - e.lastshielded = e.isshielded; - if(e.lastshielded) - { - if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); - } - else - { - if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - } -} - -string onslaught_controlpoint_waypointsprite_for_team(entity e, float t) -{ - float a; - if(t != -1) - { - a = onslaught_controlpoint_attackable(e, t); - if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW - { - if(e.team == COLOR_TEAM1) - return "ons-cp-atck-red"; - else if(e.team == COLOR_TEAM2) - return "ons-cp-atck-blue"; - else - return "ons-cp-atck-neut"; - } - else if(a == -2) // DEFEND THIS ONE NOW - { - if(e.team == COLOR_TEAM1) - return "ons-cp-dfnd-red"; - else if(e.team == COLOR_TEAM2) - return "ons-cp-dfnd-blue"; - } - else if(e.team == t || a == -1 || a == 1) // own point, or fire at it - { - if(e.team == COLOR_TEAM1) - return "ons-cp-red"; - else if(e.team == COLOR_TEAM2) - return "ons-cp-blue"; - } - else if(a == 2) // touch it - return "ons-cp-neut"; - } - else - { - if(e.team == COLOR_TEAM1) - return "ons-cp-red"; - else if(e.team == COLOR_TEAM2) - return "ons-cp-blue"; - else - return "ons-cp-neut"; - } - return ""; -} - -void onslaught_controlpoint_updatesprite(entity e) -{ - string s1, s2, s3; - s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1); - s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2); - s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1); - WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3); - - float sh; - sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2)); - - if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) - { - if(e.iscaptured) // don't mess up build bars! - { - if(sh) - { - WaypointSprite_UpdateMaxHealth(e.sprite, 0); - } - else - { - WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); - WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); - } - } - if(e.lastshielded) - { - if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); - } - else - { - if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - - e.lastteam = e.team + 2; - e.lastshielded = sh; - e.lastcaptured = e.iscaptured; - } -} - -void onslaught_generator_reset() -{ - self.team = self.team_saved; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; - self.iscaptured = TRUE; - self.islinked = TRUE; - self.isshielded = TRUE; - self.enemy.solid = SOLID_NOT; - self.think = onslaught_generator_delayed; - self.nextthink = time + 0.2; - setmodel(self, "models/onslaught/generator.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); -} - -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) - Base generator. - - spawnfunc_onslaught_link entities can target this. - -keys: -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. - */ -void spawnfunc_onslaught_generator() -{ - if (!g_onslaught) - { - remove(self); - return; - } - - entity e; - precache_model("models/onslaught/generator.md3"); - precache_model("models/onslaught/generator_shield.md3"); - precache_model("models/onslaught/generator_dmg1.md3"); - precache_model("models/onslaught/generator_dmg2.md3"); - precache_model("models/onslaught/generator_dmg3.md3"); - precache_model("models/onslaught/generator_dmg4.md3"); - precache_model("models/onslaught/generator_dmg5.md3"); - precache_model("models/onslaught/generator_dmg6.md3"); - precache_model("models/onslaught/generator_dmg7.md3"); - precache_model("models/onslaught/generator_dmg8.md3"); - precache_model("models/onslaught/generator_dmg9.md3"); - precache_model("models/onslaught/generator_dead.md3"); - precache_model("models/onslaught/shockwave.md3"); - precache_model("models/onslaught/shockwavetransring.md3"); - precache_model("models/onslaught/gen_gib1.md3"); - precache_model("models/onslaught/gen_gib2.md3"); - precache_model("models/onslaught/gen_gib3.md3"); - precache_model("models/onslaught/ons_ray.md3"); - precache_sound("onslaught/generator_decay.wav"); - precache_sound("weapons/grenade_impact.wav"); - precache_sound("weapons/rocket_impact.wav"); - precache_sound("onslaught/generator_underattack.wav"); - precache_sound("onslaught/shockwave.wav"); - precache_sound("onslaught/ons_hit1.wav"); - precache_sound("onslaught/ons_hit2.wav"); - precache_sound("onslaught/electricity_explode.wav"); - if (!self.team) - objerror("team must be set"); - self.team_saved = self.team; - self.colormap = 1024 + (self.team - 1) * 17; - self.solid = SOLID_BBOX; - self.movetype = MOVETYPE_NONE; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - setmodel(self, "models/onslaught/generator.md3"); - setsize(self, '-52 -52 -14', '52 52 75'); - setorigin(self, self.origin); - self.takedamage = DAMAGE_AIM; - self.bot_attack = TRUE; - self.event_damage = onslaught_generator_damage; - self.iscaptured = TRUE; - self.islinked = TRUE; - self.isshielded = TRUE; - // helper entity that create fx when generator is damaged - onslaught_generator_damage_spawn(self); - // spawn shield model which indicates whether this can be damaged - self.enemy = e = spawn(); - e.classname = "onslaught_generator_shield"; - e.solid = SOLID_NOT; - e.movetype = MOVETYPE_NONE; - e.effects = EF_ADDITIVE; - setmodel(e, "models/onslaught/generator_shield.md3"); - setorigin(e, self.origin); - e.colormap = self.colormap; - e.team = self.team; - self.think = onslaught_generator_delayed; - self.nextthink = time + 0.2; - InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST); - - WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0'); - WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY); - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); - - waypoint_spawnforitem(self); - - onslaught_updatelinks(); - - self.reset = onslaught_generator_reset; -} - -.float waslinked; -.float cp_bob_spd; -.vector cp_origin, cp_bob_origin, cp_bob_dmg; - -float ons_notification_time_team1; -float ons_notification_time_team2; - -void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - entity oself; - float nag; - - if (damage <= 0) - return; - if (self.owner.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (attacker.classname == "player") - { - play2(attacker, "onslaught/damageblockedbyshield.wav"); - self.pain_finished = time + 1; - } - return; - } - - if (attacker.classname == "player") - { - 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); -} diff --git a/qcsrc/server/movelib.qc b/qcsrc/server/movelib.qc index 8a6235127c..9b9f7fde01 100644 --- a/qcsrc/server/movelib.qc +++ b/qcsrc/server/movelib.qc @@ -190,7 +190,7 @@ Yed need to set v_up and v_forward (generally by calling makevectors) before cal **/ #endif -void movelib_groundalign4point(float spring_length, float spring_up, float blendrate) +void movelib_groundalign4point(float spring_length, float spring_up, float blendrate, float _max) { vector a, b, c, d, e, r, push_angle, ahead, side; @@ -227,11 +227,11 @@ void movelib_groundalign4point(float spring_length, float spring_up, float blend r = self.origin; r_z = r_z; - push_angle_x = (a_z - c_z) * 45; - push_angle_x += (b_z - d_z) * 45; + push_angle_x = (a_z - c_z) * _max; + push_angle_x += (b_z - d_z) * _max; - push_angle_z = (b_z - a_z) * 45; - push_angle_z += (d_z - c_z) * 45; + push_angle_z = (b_z - a_z) * _max; + push_angle_z += (d_z - c_z) * _max; //self.angles_x += push_angle_x * 0.95; //self.angles_z += push_angle_z * 0.95; diff --git a/qcsrc/server/mutators/base.qc b/qcsrc/server/mutators/base.qc index fdf24edb75..9be5745cb5 100644 --- a/qcsrc/server/mutators/base.qc +++ b/qcsrc/server/mutators/base.qc @@ -59,6 +59,7 @@ float CallbackChain_Remove(entity cb, float() func) entity e; float n; n = 0; + order = 0; for(e = cb; e.cbc_next; e = e.cbc_next) { while(e.cbc_next.cbc_func == func) diff --git a/qcsrc/server/mutators/base.qh b/qcsrc/server/mutators/base.qh index 137eebadb8..29a8e4e0b5 100644 --- a/qcsrc/server/mutators/base.qh +++ b/qcsrc/server/mutators/base.qh @@ -205,3 +205,55 @@ MUTATOR_HOOKABLE(SV_StartFrame); MUTATOR_HOOKABLE(SetModname); // OUT string modname; // name of the mutator/mod if it warrants showing as such in the server browser + +MUTATOR_HOOKABLE(SetWeaponreplace); + // IN + entity self; // map entity + entity other; // weapon info + // IN+OUT + string ret_string; + +MUTATOR_HOOKABLE(PortalTeleport); + // called whenever a player goes through a portal gun teleport + // allows you to strip a player of an item if they go through the teleporter to help prevent cheating + // INPUT + entity self; + +MUTATOR_HOOKABLE(HelpMePing); + // called whenever a player uses impulse 33 (help me) in cl_impulse.qc + // normally help me ping uses self.waypointsprite_attachedforcarrier, + // but if your mutator uses something different then you can handle it + // in a special manner using this hook + // INPUT + entity self; // the player who pressed impulse 33 + +MUTATOR_HOOKABLE(VehicleEnter); + // called when a player enters a vehicle + // allows mutators to set special settings in this event + // INPUT + entity vh_player; // player + entity vh_vehicle; // vehicle + +MUTATOR_HOOKABLE(VehicleExit); + // called when a player exits a vehicle + // allows mutators to set special settings in this event + // INPUT + entity vh_player; // player + entity vh_vehicle; // vehicle + +MUTATOR_HOOKABLE(AbortSpeedrun); + // called when a speedrun is aborted and the player is teleported back to start position + // INPUT + entity self; // player + +MUTATOR_HOOKABLE(ItemTouch); + // called at when a item is touched. Called early, can edit item properties. + entity self; // item + entity other; // player + +MUTATOR_HOOKABLE(ClientConnect); + // called at when a player connect + entity self; // player + +MUTATOR_HOOKABLE(HavocBot_ChooseRule); + entity self; diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc new file mode 100644 index 0000000000..f453cc22ad --- /dev/null +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -0,0 +1,2211 @@ +// ================================================================ +// Official capture the flag game mode coding, reworked by Samual +// Last updated: September, 2012 +// ================================================================ + +void ctf_FakeTimeLimit(entity e, float t) +{ + msg_entity = e; + WriteByte(MSG_ONE, 3); // svc_updatestat + WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT + if(t < 0) + WriteCoord(MSG_ONE, autocvar_timelimit); + else + WriteCoord(MSG_ONE, (t + 1) / 60); +} + +void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later +{ + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); +} + +string ctf_CaptureRecord(entity flag, entity player) +{ + float cap_time, cap_record, success; + string cap_message, refername; + + if((autocvar_g_ctf_captimerecord_always) || (player_count - currentbots)) + { + cap_record = ctf_captimerecord; + cap_time = (time - flag.ctf_pickuptime); + + refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname")); + refername = ((refername == player.netname) ? "their" : strcat(refername, "^7's")); + + if(!ctf_captimerecord) + { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds"); success = TRUE; } + else if(cap_time < cap_record) + { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, breaking ", refername, " previous record of ", ftos_decimals(cap_record, 2), " seconds"); success = TRUE; } + else + { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, failing to break ", refername, " record of ", ftos_decimals(cap_record, 2), " seconds"); success = FALSE; } + + if(success) + { + ctf_captimerecord = cap_time; + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time)); + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname); + write_recordmarker(player, (time - cap_time), cap_time); + } + } + + return cap_message; +} + +void ctf_FlagcarrierWaypoints(entity player) +{ + WaypointSprite_Spawn("flagcarrier", 0, 0, player, FLAG_WAYPOINT_OFFSET, world, player.team, player, wps_flagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_FLAGCARRIER(player.team)); + WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent) * 2); + WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent)); + WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team)); +} + +void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnrate) +{ + float current_distance = vlen((('1 0 0' * to_x) + ('0 1 0' * to_y)) - (('1 0 0' * from_x) + ('0 1 0' * from_y))); // for the sake of this check, exclude Z axis + float initial_height = min(autocvar_g_ctf_pass_arc_max, (flag.pass_distance * tanh(autocvar_g_ctf_pass_arc))); + float current_height = (initial_height * min(1, (current_distance / flag.pass_distance))); + //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n"); + + vector targpos; + if(current_height) // make sure we can actually do this arcing path + { + targpos = (to + ('0 0 1' * current_height)); + WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag); + if(trace_fraction < 1) + { + //print("normal arc line failed, trying to find new pos..."); + WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, flag); + targpos = (trace_endpos + FLAG_PASS_ARC_OFFSET); + WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag); + if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ } + /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */ + } + } + else { targpos = to; } + + //flag.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y)); + + vector desired_direction = normalize(targpos - from); + if(turnrate) { flag.velocity = (normalize(normalize(flag.velocity) + (desired_direction * autocvar_g_ctf_pass_turnrate)) * autocvar_g_ctf_pass_velocity); } + else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); } +} + +float ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer) +{ + if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min) + { + // directional tracing only + float spreadlimit; + makevectors(passer_angle); + + // find the closest point on the enemy to the center of the attack + float ang; // angle between shotdir and h + float h; // hypotenuse, which is the distance between attacker to head + float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin + + h = vlen(head_center - passer_center); + ang = acos(dotproduct(normalize(head_center - passer_center), v_forward)); + a = h * cos(ang); + + vector nearest_on_line = (passer_center + a * v_forward); + float distance_from_line = vlen(nearest_to_passer - nearest_on_line); + + spreadlimit = (autocvar_g_ctf_pass_radius ? min(1, (vlen(passer_center - nearest_on_line) / autocvar_g_ctf_pass_radius)) : 1); + spreadlimit = (autocvar_g_ctf_pass_directional_min * (1 - spreadlimit) + autocvar_g_ctf_pass_directional_max * spreadlimit); + + if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(head_center - passer_center) - v_forward) * RAD2DEG) <= 90)) + { return TRUE; } + else + { return FALSE; } + } + else { return TRUE; } +} + + +// ======================= +// CaptureShield Functions +// ======================= + +float ctf_CaptureShield_CheckStatus(entity p) +{ + float s, se; + entity e; + float players_worseeq, players_total; + + if(ctf_captureshield_max_ratio <= 0) + return FALSE; + + s = PlayerScore_Add(p, SP_SCORE, 0); + if(s >= -ctf_captureshield_min_negscore) + return FALSE; + + players_total = players_worseeq = 0; + FOR_EACH_PLAYER(e) + { + if(IsDifferentTeam(e, p)) + continue; + se = PlayerScore_Add(e, SP_SCORE, 0); + if(se <= s) + ++players_worseeq; + ++players_total; + } + + // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse + // use this rule here + + if(players_worseeq >= players_total * ctf_captureshield_max_ratio) + return FALSE; + + return TRUE; +} + +void ctf_CaptureShield_Update(entity player, float wanted_status) +{ + float updated_status = ctf_CaptureShield_CheckStatus(player); + if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only + { + if(updated_status) // TODO csqc notifier for this // Samual: How? + Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.", 5, 0); + else + Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0); + + player.ctf_captureshielded = updated_status; + } +} + +float ctf_CaptureShield_Customize() +{ + if(!other.ctf_captureshielded) { return FALSE; } + if(!IsDifferentTeam(self, other)) { return FALSE; } + + return TRUE; +} + +void ctf_CaptureShield_Touch() +{ + if(!other.ctf_captureshielded) { return; } + if(!IsDifferentTeam(self, other)) { return; } + + vector mymid = (self.absmin + self.absmax) * 0.5; + vector othermid = (other.absmin + other.absmax) * 0.5; + + Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force); + Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0); +} + +void ctf_CaptureShield_Spawn(entity flag) +{ + entity shield = spawn(); + + shield.enemy = self; + shield.team = self.team; + shield.touch = ctf_CaptureShield_Touch; + shield.customizeentityforclient = ctf_CaptureShield_Customize; + shield.classname = "ctf_captureshield"; + shield.effects = EF_ADDITIVE; + shield.movetype = MOVETYPE_NOCLIP; + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = 0.5; + + setorigin(shield, self.origin); + setmodel(shield, "models/ctf/shield.md3"); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); +} + + +// ==================== +// Drop/Pass/Throw Code +// ==================== + +void ctf_Handle_Drop(entity flag, entity player, float droptype) +{ + // declarations + player = (player ? player : flag.pass_sender); + + // main + flag.movetype = MOVETYPE_TOSS; + flag.takedamage = DAMAGE_YES; + flag.angles = '0 0 0'; + flag.health = flag.max_flag_health; + flag.ctf_droptime = time; + flag.ctf_dropper = player; + flag.ctf_status = FLAG_DROPPED; + + // messages and sounds + Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO); + sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTN_NONE); + ctf_EventLog("dropped", player.team, player); + + // scoring + PlayerTeamScore_AddScore(player, -autocvar_g_ctf_score_penalty_drop); + PlayerScore_Add(player, SP_CTF_DROPS, 1); + + // waypoints + if(autocvar_g_ctf_flag_dropped_waypoint) + WaypointSprite_Spawn("flagdropped", 0, 0, flag, FLAG_WAYPOINT_OFFSET, world, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, TRUE, RADARICON_FLAG, WPCOLOR_DROPPEDFLAG(flag.team)); + + if(autocvar_g_ctf_flag_return_time || (autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health)) + { + WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health); + WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); + } + + player.throw_antispam = time + autocvar_g_ctf_pass_wait; + + if(droptype == DROP_PASS) + { + flag.pass_distance = 0; + flag.pass_sender = world; + flag.pass_target = world; + } +} + +void ctf_Handle_Retrieve(entity flag, entity player) +{ + entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players + entity sender = flag.pass_sender; + + // transfer flag to player + flag.owner = player; + flag.owner.flagcarried = flag; + + // reset flag + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + flag.movetype = MOVETYPE_NONE; + flag.takedamage = DAMAGE_NO; + flag.solid = SOLID_NOT; + flag.angles = '0 0 0'; + flag.ctf_status = FLAG_CARRY; + + // messages and sounds + sound(player, CH_TRIGGER, flag.snd_flag_pass, VOL_BASE, ATTN_NORM); + ctf_EventLog("receive", flag.team, player); + + FOR_EACH_REALPLAYER(tmp_player) + { + if(tmp_player == sender) + centerprint(tmp_player, strcat("You passed the ", flag.netname, " to ", player.netname)); + else if(tmp_player == player) + centerprint(tmp_player, strcat("You received the ", flag.netname, " from ", sender.netname)); + else if(!IsDifferentTeam(tmp_player, sender)) + centerprint(tmp_player, strcat(sender.netname, " passed the ", flag.netname, " to ", player.netname)); + } + + // create new waypoint + ctf_FlagcarrierWaypoints(player); + + sender.throw_antispam = time + autocvar_g_ctf_pass_wait; + player.throw_antispam = sender.throw_antispam; + + flag.pass_distance = 0; + flag.pass_sender = world; + flag.pass_target = world; +} + +void ctf_Handle_Throw(entity player, entity receiver, float droptype) +{ + entity flag = player.flagcarried; + vector targ_origin, flag_velocity; + + if(!flag) { return; } + if((droptype == DROP_PASS) && !receiver) { return; } + + if(flag.speedrunning) { ctf_RespawnFlag(flag); return; } + + // reset the flag + setattachment(flag, world, ""); + setorigin(flag, player.origin + FLAG_DROP_OFFSET); + flag.owner.flagcarried = world; + flag.owner = world; + flag.solid = SOLID_TRIGGER; + flag.ctf_dropper = player; + flag.ctf_droptime = time; + + flag.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS + + switch(droptype) + { + case DROP_PASS: + { + // warpzone support: + // for the examples, we assume player -> wz1 -> ... -> wzn -> receiver + // findradius has already put wzn ... wz1 into receiver's warpzone parameters! + WarpZone_RefSys_Copy(flag, receiver); + WarpZone_RefSys_AddInverse(flag, receiver); // wz1^-1 ... wzn^-1 receiver + targ_origin = WarpZone_RefSys_TransformOrigin(receiver, flag, (0.5 * (receiver.absmin + receiver.absmax))); // this is target origin as seen by the flag + + flag.pass_distance = vlen((('1 0 0' * targ_origin_x) + ('0 1 0' * targ_origin_y)) - (('1 0 0' * player.origin_x) + ('0 1 0' * player.origin_y))); // for the sake of this check, exclude Z axis + ctf_CalculatePassVelocity(flag, targ_origin, player.origin, FALSE); + + // main + flag.movetype = MOVETYPE_FLY; + flag.takedamage = DAMAGE_NO; + flag.pass_sender = player; + flag.pass_target = receiver; + flag.ctf_status = FLAG_PASSING; + + // other + sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTN_NORM); + WarpZone_TrailParticles(world, particleeffectnum(flag.passeffect), player.origin, targ_origin); + ctf_EventLog("pass", flag.team, player); + break; + } + + case DROP_THROW: + { + makevectors((player.v_angle_y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle_x, autocvar_g_ctf_throw_angle_max) * '1 0 0')); + + flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & IT_STRENGTH) ? autocvar_g_ctf_throw_strengthmultiplier : 1))); + flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, FALSE); + ctf_Handle_Drop(flag, player, droptype); + break; + } + + case DROP_RESET: + { + flag.velocity = '0 0 0'; // do nothing + break; + } + + default: + case DROP_NORMAL: + { + flag.velocity = W_CalculateProjectileVelocity(player.velocity, (('0 0 1' * autocvar_g_ctf_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_ctf_drop_velocity_side)), FALSE); + ctf_Handle_Drop(flag, player, droptype); + break; + } + } + + // kill old waypointsprite + WaypointSprite_Ping(player.wps_flagcarrier); + WaypointSprite_Kill(player.wps_flagcarrier); + + if(player.wps_enemyflagcarrier) + WaypointSprite_Kill(player.wps_enemyflagcarrier); + + // captureshield + ctf_CaptureShield_Update(player, 0); // shield player from picking up flag +} + + +// ============== +// Event Handlers +// ============== + +void ctf_Handle_Capture(entity flag, entity toucher, float capturetype) +{ + entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher); + entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper); + float old_time, new_time; + + if not(player) { return; } // without someone to give the reward to, we can't possibly cap + + // messages and sounds + Send_KillNotification(player.netname, enemy_flag.netname, ctf_CaptureRecord(enemy_flag, player), INFO_CAPTUREFLAG, MSG_INFO); + sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTN_NONE); + + switch(capturetype) + { + case CAPTURE_NORMAL: ctf_EventLog("capture", enemy_flag.team, player); break; + case CAPTURE_DROPPED: ctf_EventLog("droppedcapture", enemy_flag.team, player); break; + default: break; + } + + // scoring + PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_capture); + PlayerTeamScore_Add(player, SP_CTF_CAPS, ST_CTF_CAPS, 1); + + old_time = PlayerScore_Add(player, SP_CTF_CAPTIME, 0); + new_time = TIME_ENCODE(time - enemy_flag.ctf_pickuptime); + if(!old_time || new_time < old_time) + PlayerScore_Add(player, SP_CTF_CAPTIME, new_time - old_time); + + // effects + pointparticles(particleeffectnum(flag.capeffect), flag.origin, '0 0 0', 1); + //shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1); + + // other + if(capturetype == CAPTURE_NORMAL) + { + WaypointSprite_Kill(player.wps_flagcarrier); + if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); } + + if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper)) + { PlayerTeamScore_AddScore(enemy_flag.ctf_dropper, autocvar_g_ctf_score_capture_assist); } + } + + // reset the flag + player.next_take_time = time + autocvar_g_ctf_flag_collect_delay; + ctf_RespawnFlag(enemy_flag); +} + +void ctf_Handle_Return(entity flag, entity player) +{ + // messages and sounds + //centerprint(player, strcat("You returned the ", flag.netname)); + Send_KillNotification(player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO); + sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTN_NONE); + ctf_EventLog("return", flag.team, player); + + // scoring + PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return + PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns + + TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it + + if(flag.ctf_dropper) + { + PlayerScore_Add(flag.ctf_dropper, SP_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag + ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag + flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time + } + + // reset the flag + ctf_RespawnFlag(flag); +} + +void ctf_Handle_Pickup(entity flag, entity player, float pickuptype) +{ + // declarations + entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players + string verbosename; // holds the name of the player OR no name at all for printing in the centerprints + float pickup_dropped_score; // used to calculate dropped pickup score + + // attach the flag to the player + flag.owner = player; + player.flagcarried = flag; + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + + // flag setup + flag.movetype = MOVETYPE_NONE; + flag.takedamage = DAMAGE_NO; + flag.solid = SOLID_NOT; + flag.angles = '0 0 0'; + flag.ctf_status = FLAG_CARRY; + + switch(pickuptype) + { + case PICKUP_BASE: flag.ctf_pickuptime = time; break; // used for timing runs + case PICKUP_DROPPED: flag.health = flag.max_flag_health; break; // reset health/return timelimit + default: break; + } + + // messages and sounds + Send_KillNotification (player.netname, flag.netname, "", INFO_GOTFLAG, MSG_INFO); + sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTN_NONE); + verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat(Team_ColorCode(player.team), "(^7", player.netname, Team_ColorCode(player.team), ") ") : ""); + + FOR_EACH_REALPLAYER(tmp_player) + { + if(tmp_player == player) + { + centerprint(tmp_player, strcat("You got the ", flag.netname, "!")); + //if(ctf_stalemate) { centerprint(tmp_player, "Stalemate! Enemies can see you on radar!"); } + } + //else if(!IsDifferentTeam(tmp_player, player)) + // centerprint(tmp_player, strcat("Your ", Team_ColorCode(player.team), "team mate ", verbosename, "^7got the flag! Protect them!")); + else if(!IsDifferentTeam(tmp_player, flag)) + centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!")); + } + + // scoring + PlayerScore_Add(player, SP_CTF_PICKUPS, 1); + switch(pickuptype) + { + case PICKUP_BASE: + { + PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_pickup_base); + ctf_EventLog("steal", flag.team, player); + break; + } + + case PICKUP_DROPPED: + { + pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1); + pickup_dropped_score = floor((autocvar_g_ctf_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_ctf_score_pickup_dropped_early * pickup_dropped_score) + 0.5); + dprint("pickup_dropped_score is ", ftos(pickup_dropped_score), "\n"); + PlayerTeamScore_AddScore(player, pickup_dropped_score); + ctf_EventLog("pickup", flag.team, player); + break; + } + + default: break; + } + + // speedrunning + if(pickuptype == PICKUP_BASE) + { + flag.speedrunning = player.speedrunning; // if speedrunning, flag will flag-return and teleport the owner back after the record + if((player.speedrunning) && (ctf_captimerecord)) + ctf_FakeTimeLimit(player, time + ctf_captimerecord); + } + + // effects + pointparticles(particleeffectnum(flag.toucheffect), player.origin, '0 0 0', 1); + + // waypoints + if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); } + ctf_FlagcarrierWaypoints(player); + WaypointSprite_Ping(player.wps_flagcarrier); +} + + +// =================== +// Main Flag Functions +// =================== + +void ctf_CheckFlagReturn(entity flag, float returntype) +{ + if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING)) + { + if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); } + + if((flag.health <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time)) + { + switch(returntype) + { + case RETURN_DROPPED: bprint("The ", flag.netname, " was dropped in the base and returned itself\n"); break; + case RETURN_DAMAGE: bprint("The ", flag.netname, " was destroyed and returned to base\n"); break; + case RETURN_SPEEDRUN: bprint("The ", flag.netname, " became impatient after ", ftos_decimals(ctf_captimerecord, 2), " seconds and returned itself\n"); break; + case RETURN_NEEDKILL: bprint("The ", flag.netname, " fell somewhere it couldn't be reached and returned to base\n"); break; + + default: + case RETURN_TIMEOUT: + { bprint("The ", flag.netname, " has returned to base\n"); break; } + } + sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTN_NONE); + ctf_EventLog("returned", flag.team, world); + ctf_RespawnFlag(flag); + } + } +} + +void ctf_CheckStalemate(void) +{ + // declarations + float stale_red_flags, stale_blue_flags; + entity tmp_entity; + + entity ctf_staleflaglist; // reset the list, we need to build the list each time this function runs + + // build list of stale flags + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + { + if(autocvar_g_ctf_stalemate) + if(tmp_entity.ctf_status != FLAG_BASE) + if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time) + { + tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist + ctf_staleflaglist = tmp_entity; + + switch(tmp_entity.team) + { + case COLOR_TEAM1: ++stale_red_flags; break; + case COLOR_TEAM2: ++stale_blue_flags; break; + } + } + } + + if(stale_red_flags && stale_blue_flags) + ctf_stalemate = TRUE; + else if((!stale_red_flags && !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 2) + { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; } + else if((!stale_red_flags || !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 1) + { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; } + + // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary + if(ctf_stalemate) + { + for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext) + { + if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier)) + WaypointSprite_Spawn("enemyflagcarrier", 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, tmp_entity.team, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team)); + } + + if not(wpforenemy_announced) + { + FOR_EACH_REALPLAYER(tmp_entity) + if(tmp_entity.flagcarried) + centerprint(tmp_entity, "Stalemate! Enemies can now see you on radar!"); + else + centerprint(tmp_entity, "Stalemate! Flag carriers can now be seen by enemies on radar!"); + + wpforenemy_announced = TRUE; + } + } +} + +void ctf_FlagDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + // automatically kill the flag and return it + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + return; + } + if(autocvar_g_ctf_flag_return_damage) + { + // reduce health and check if it should be returned + self.health = self.health - damage; + ctf_CheckFlagReturn(self, RETURN_DAMAGE); + return; + } +} + +void ctf_FlagThink() +{ + // declarations + entity tmp_entity; + + self.nextthink = time + FLAG_THINKRATE; // only 5 fps, more is unnecessary. + + // captureshield + if(self == ctf_worldflaglist) // only for the first flag + FOR_EACH_CLIENT(tmp_entity) + ctf_CaptureShield_Update(tmp_entity, 1); // release shield only + + // sanity checks + if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished + dprint("wtf the flag got squashed?\n"); + tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self); + if(!trace_startsolid) // can we resize it without getting stuck? + setsize(self, FLAG_MIN, FLAG_MAX); } + + switch(self.ctf_status) // reset flag angles in case warpzones adjust it + { + case FLAG_DROPPED: + { + self.angles = '0 0 0'; + break; + } + + default: break; + } + + // main think method + switch(self.ctf_status) + { + case FLAG_BASE: + { + if(autocvar_g_ctf_dropped_capture_radius) + { + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + if(tmp_entity.ctf_status == FLAG_DROPPED) + if(vlen(self.origin - tmp_entity.origin) < autocvar_g_ctf_dropped_capture_radius) + if(time > tmp_entity.ctf_droptime + autocvar_g_ctf_dropped_capture_delay) + ctf_Handle_Capture(self, tmp_entity, CAPTURE_DROPPED); + } + return; + } + + case FLAG_DROPPED: + { + if(autocvar_g_ctf_flag_dropped_floatinwater) + { + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if(pointcontents(midpoint + FLAG_FLOAT_OFFSET) == CONTENT_WATER) + { self.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; } + else + { self.movetype = MOVETYPE_FLY; } + } + else if(self.movetype == MOVETYPE_FLY) { self.movetype = MOVETYPE_TOSS; } + } + if(autocvar_g_ctf_flag_return_dropped) + { + if((vlen(self.origin - self.ctf_spawnorigin) <= autocvar_g_ctf_flag_return_dropped) || (autocvar_g_ctf_flag_return_dropped == -1)) + { + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_DROPPED); + return; + } + } + if(autocvar_g_ctf_flag_return_time) + { + self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE); + ctf_CheckFlagReturn(self, RETURN_TIMEOUT); + return; + } + return; + } + + case FLAG_CARRY: + { + if(self.speedrunning && ctf_captimerecord && (time >= self.ctf_pickuptime + ctf_captimerecord)) + { + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_SPEEDRUN); + + tmp_entity = self; + self = self.owner; + self.impulse = CHIMPULSE_SPEEDRUN; // move the player back to the waypoint they set + ImpulseCommands(); + self = tmp_entity; + } + if(autocvar_g_ctf_stalemate) + { + if(time >= wpforenemy_nextthink) + { + ctf_CheckStalemate(); + wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check) + } + } + return; + } + + case FLAG_PASSING: + { + vector targ_origin = ((self.pass_target.absmin + self.pass_target.absmax) * 0.5); + targ_origin = WarpZone_RefSys_TransformOrigin(self.pass_target, self, targ_origin); // origin of target as seen by the flag (us) + WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self); + + if((self.pass_target == world) + || (self.pass_target.deadflag != DEAD_NO) + || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius) + || ((trace_fraction < 1) && (trace_ent != self.pass_target)) + || (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit)) + { + // give up, pass failed + ctf_Handle_Drop(self, world, DROP_PASS); + } + else + { + // still a viable target, go for it + ctf_CalculatePassVelocity(self, targ_origin, self.origin, TRUE); + } + return; + } + + default: // this should never happen + { + dprint("ctf_FlagThink(): Flag exists with no status?\n"); + return; + } + } +} + +void ctf_FlagTouch() +{ + if(gameover) { return; } + + entity toucher = other; + + // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces + if(ITEM_TOUCH_NEEDKILL()) + { + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + return; + } + + // special touch behaviors + if(toucher.vehicle_flags & VHF_ISVEHICLE) + { + if(autocvar_g_ctf_allow_vehicle_touch) + toucher = toucher.owner; // the player is actually the vehicle owner, not other + else + return; // do nothing + } + else if(toucher.classname != "player") // The flag just touched an object, most likely the world + { + if(time > self.wait) // if we haven't in a while, play a sound/effect + { + pointparticles(particleeffectnum(self.toucheffect), self.origin, '0 0 0', 1); + sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTN_NORM); + self.wait = time + FLAG_TOUCHRATE; + } + return; + } + else if(toucher.deadflag != DEAD_NO) { return; } + + switch(self.ctf_status) + { + case FLAG_BASE: + { + if(!IsDifferentTeam(toucher, self) && (toucher.flagcarried) && IsDifferentTeam(toucher.flagcarried, self)) + ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base + else if(IsDifferentTeam(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time)) + ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag + break; + } + + case FLAG_DROPPED: + { + if(!IsDifferentTeam(toucher, self)) + ctf_Handle_Return(self, toucher); // toucher just returned his own flag + else if((!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) + ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag + break; + } + + case FLAG_CARRY: + { + dprint("Someone touched a flag even though it was being carried?\n"); + break; + } + + case FLAG_PASSING: + { + if((toucher.classname == "player") && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender)) + { + if(IsDifferentTeam(toucher, self.pass_sender)) + ctf_Handle_Return(self, toucher); + else + ctf_Handle_Retrieve(self, toucher); + } + break; + } + } +} + +.float last_respawn; +void ctf_RespawnFlag(entity flag) +{ + // check for flag respawn being called twice in a row + if(flag.last_respawn > time - 0.5) + { backtrace("flag respawn called twice quickly! please notify Samual about this..."); } + + flag.last_respawn = time; + + // reset the player (if there is one) + if((flag.owner) && (flag.owner.flagcarried == flag)) + { + if(flag.owner.wps_enemyflagcarrier) + WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier); + + WaypointSprite_Kill(flag.wps_flagcarrier); + + flag.owner.flagcarried = world; + + if(flag.speedrunning) + ctf_FakeTimeLimit(flag.owner, -1); + } + + if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped)) + { WaypointSprite_Kill(flag.wps_flagdropped); } + + // reset the flag + setattachment(flag, world, ""); + setorigin(flag, flag.ctf_spawnorigin); + + flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS); + flag.takedamage = DAMAGE_NO; + flag.health = flag.max_flag_health; + flag.solid = SOLID_TRIGGER; + flag.velocity = '0 0 0'; + flag.angles = flag.mangle; + flag.flags = FL_ITEM | FL_NOTARGET; + + flag.ctf_status = FLAG_BASE; + flag.owner = world; + flag.pass_distance = 0; + flag.pass_sender = world; + flag.pass_target = world; + flag.ctf_dropper = world; + flag.ctf_pickuptime = 0; + flag.ctf_droptime = 0; +} + +void ctf_Reset() +{ + if(self.owner) + if(self.owner.classname == "player") + ctf_Handle_Throw(self.owner, world, DROP_RESET); + + ctf_RespawnFlag(self); +} + +void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup() +{ + // bot waypoints + waypoint_spawnforitem_force(self, self.origin); + self.nearestwaypointtimeout = 0; // activate waypointing again + self.bot_basewaypoint = self.nearestwaypoint; + + // waypointsprites + WaypointSprite_SpawnFixed(((self.team == COLOR_TEAM1) ? "redbase" : "bluebase"), self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, FALSE)); + WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, FALSE)); + + // captureshield setup + ctf_CaptureShield_Spawn(self); +} + +void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc +{ + // declarations + teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1. + self = flag; // for later usage with droptofloor() + + // main setup + flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist + ctf_worldflaglist = flag; + + setattachment(flag, world, ""); + + flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag"); + flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue) + flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough) + flag.classname = "item_flag_team"; + flag.target = "###item###"; // wut? + flag.flags = FL_ITEM | FL_NOTARGET; + flag.solid = SOLID_TRIGGER; + flag.takedamage = DAMAGE_NO; + flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale; + flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100); + flag.health = flag.max_flag_health; + flag.event_damage = ctf_FlagDamage; + flag.pushable = TRUE; + flag.teleportable = TELEPORT_NORMAL; + flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable; + flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable; + flag.velocity = '0 0 0'; + flag.mangle = flag.angles; + flag.reset = ctf_Reset; + flag.touch = ctf_FlagTouch; + flag.think = ctf_FlagThink; + flag.nextthink = time + FLAG_THINKRATE; + flag.ctf_status = FLAG_BASE; + + if(!flag.model) { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); } + if(!flag.scale) { flag.scale = FLAG_SCALE; } + if(!flag.skin) { flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin); } + if(!flag.toucheffect) { flag.toucheffect = ((teamnumber) ? "redflag_touch" : "blueflag_touch"); } + if(!flag.passeffect) { flag.passeffect = ((teamnumber) ? "red_pass" : "blue_pass"); } + if(!flag.capeffect) { flag.capeffect = ((teamnumber) ? "red_cap" : "blue_cap"); } + + // sound + if(!flag.snd_flag_taken) { flag.snd_flag_taken = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); } + if(!flag.snd_flag_returned) { flag.snd_flag_returned = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); } + if(!flag.snd_flag_capture) { flag.snd_flag_capture = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag + if(!flag.snd_flag_respawn) { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match. + if(!flag.snd_flag_dropped) { flag.snd_flag_dropped = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); } + if(!flag.snd_flag_touch) { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound + if(!flag.snd_flag_pass) { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here + + // precache + precache_sound(flag.snd_flag_taken); + precache_sound(flag.snd_flag_returned); + precache_sound(flag.snd_flag_capture); + precache_sound(flag.snd_flag_respawn); + precache_sound(flag.snd_flag_dropped); + precache_sound(flag.snd_flag_touch); + precache_sound(flag.snd_flag_pass); + precache_model(flag.model); + precache_model("models/ctf/shield.md3"); + precache_model("models/ctf/shockwavetransring.md3"); + + // appearence + setmodel(flag, flag.model); // precision set below + setsize(flag, FLAG_MIN, FLAG_MAX); + setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET)); + + if(autocvar_g_ctf_flag_glowtrails) + { + flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue + flag.glow_size = 25; + flag.glow_trail = 1; + } + + flag.effects |= EF_LOWPRECISION; + if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; } + if(autocvar_g_ctf_dynamiclights) { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); } + + // flag placement + if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location + { + flag.dropped_origin = flag.origin; + flag.noalign = TRUE; + flag.movetype = MOVETYPE_NONE; + } + else // drop to floor, automatically find a platform and set that as spawn origin + { + flag.noalign = FALSE; + self = flag; + droptofloor(); + flag.movetype = MOVETYPE_TOSS; + } + + InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION); +} + + +// ================ +// Bot player logic +// ================ + +// NOTE: LEGACY CODE, needs to be re-written! + +void havocbot_calculate_middlepoint() +{ + entity f; + vector s = '0 0 0'; + vector fo = '0 0 0'; + float n = 0; + + f = ctf_worldflaglist; + while (f) + { + fo = f.origin; + s = s + fo; + f = f.ctf_worldflagnext; + } + if(!n) + return; + havocbot_ctf_middlepoint = s * (1.0 / n); + havocbot_ctf_middlepoint_radius = vlen(fo - havocbot_ctf_middlepoint); +} + + +entity havocbot_ctf_find_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team == f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +entity havocbot_ctf_find_enemy_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (bot.team != f.team) + return f; + f = f.ctf_worldflagnext; + } + return world; +} + +float havocbot_ctf_teamcount(entity bot, vector org, float tc_radius) +{ + if not(teamplay) + return 0; + + float c = 0; + entity head; + + FOR_EACH_PLAYER(head) + { + if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot) + continue; + + if(vlen(head.origin - org) < tc_radius) + ++c; + } + + return c; +} + +void havocbot_goalrating_ctf_ourflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourbase(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team == head.team) + break; + head = head.ctf_worldflagnext; + } + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemyflag(float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (self.team != head.team) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(head, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemybase(float ratingscale) +{ + if not(bot_waypoints_for_items) + { + havocbot_goalrating_ctf_enemyflag(ratingscale); + return; + } + + entity head; + + head = havocbot_ctf_find_enemy_flag(self); + + if not(head) + return; + + navigation_routerating(head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourstolenflag(float ratingscale) +{ + entity mf; + + mf = havocbot_ctf_find_flag(self); + + if(mf.ctf_status == FLAG_BASE) + return; + + if(mf.tag_entity) + navigation_routerating(mf.tag_entity, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float df_radius) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + // flag is out in the field + if(head.ctf_status != FLAG_BASE) + if(head.tag_entity==world) // dropped + { + if(df_radius) + { + if(vlen(org-head.origin) 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(distance1000) + if(checkpvs(self.origin,closestplayer)||random()<0.5) + havocbot_goalrating_ctf_ourbase(30000); + + havocbot_goalrating_ctf_ourstolenflag(20000); + havocbot_goalrating_ctf_droppedflags(20000, org, mp_radius); + havocbot_goalrating_enemyplayers(15000, org, mp_radius); + havocbot_goalrating_items(10000, org, mp_radius); + havocbot_goalrating_items(5000, self.origin, 10000); + navigation_goalrating_end(); + } +} + +void havocbot_role_ctf_setrole(entity bot, float role) +{ + dprint(strcat(bot.netname," switched to ")); + switch(role) + { + case HAVOCBOT_CTF_ROLE_CARRIER: + dprint("carrier"); + bot.havocbot_role = havocbot_role_ctf_carrier; + bot.havocbot_role_timeout = 0; + bot.havocbot_cantfindflag = time + 10; + bot.bot_strategytime = 0; + break; + case HAVOCBOT_CTF_ROLE_DEFENSE: + dprint("defense"); + bot.havocbot_role = havocbot_role_ctf_defense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_MIDDLE: + dprint("middle"); + bot.havocbot_role = havocbot_role_ctf_middle; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_OFFENSE: + dprint("offense"); + bot.havocbot_role = havocbot_role_ctf_offense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_RETRIEVER: + dprint("retriever"); + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_retriever; + bot.havocbot_role_timeout = time + 10; + bot.bot_strategytime = 0; + break; + case HAVOCBOT_CTF_ROLE_ESCORT: + dprint("escort"); + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_escort; + bot.havocbot_role_timeout = time + 30; + bot.bot_strategytime = 0; + break; + } + dprint("\n"); +} + + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink) +{ + entity flag; + + // initially clear items so they can be set as necessary later. + self.items &~= (IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST + | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED); + + // scan through all the flags and notify the client about them + for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + switch(flag.ctf_status) + { + case FLAG_PASSING: + case FLAG_CARRY: + { + if((flag.owner == self) || (flag.pass_sender == self)) + self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag + else + self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag + break; + } + case FLAG_DROPPED: + { + self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_LOST : IT_BLUE_FLAG_LOST); // lost: the flag is dropped somewhere on the map + break; + } + } + } + + // item for stopping players from capturing the flag too often + if(self.ctf_captureshielded) + self.items |= IT_CTF_SHIELDED; + + // update the health of the flag carrier waypointsprite + if(self.wps_flagcarrier) + WaypointSprite_UpdateHealth(self.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent)); + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc +{ + if(frag_attacker.flagcarried) // if the attacker is a flagcarrier + { + if(frag_target == frag_attacker) // damage done to yourself + { + frag_damage *= autocvar_g_ctf_flagcarrier_selfdamagefactor; + frag_force *= autocvar_g_ctf_flagcarrier_selfforcefactor; + } + else // damage done to everyone else + { + frag_damage *= autocvar_g_ctf_flagcarrier_damagefactor; + frag_force *= autocvar_g_ctf_flagcarrier_forcefactor; + } + } + else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && IsDifferentTeam(frag_target, frag_attacker)) // if the target is a flagcarrier + { + if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent))) + if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time) + { + frag_target.wps_helpme_time = time; + WaypointSprite_HelpMePing(frag_target.wps_flagcarrier); + } + } + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_PlayerDies) +{ + if((frag_attacker != frag_target) && (frag_attacker.classname == "player") && (frag_target.flagcarried)) + { + PlayerTeamScore_AddScore(frag_attacker, autocvar_g_ctf_score_kill); + PlayerScore_Add(frag_attacker, SP_CTF_FCKILLS, 1); + } + + if(frag_target.flagcarried) + { ctf_Handle_Throw(frag_target, world, DROP_NORMAL); } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill) +{ + frag_score = 0; + return (autocvar_g_ctf_ignore_frags); // no frags counted in ctf if this is true +} + +MUTATOR_HOOKFUNCTION(ctf_RemovePlayer) +{ + entity flag; // temporary entity for the search method + + if(self.flagcarried) + { ctf_Handle_Throw(self, world, DROP_NORMAL); } + + for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + if(flag.pass_sender == self) { flag.pass_sender = world; } + if(flag.pass_target == self) { flag.pass_target = world; } + if(flag.ctf_dropper == self) { flag.ctf_dropper = world; } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_PortalTeleport) +{ + if(self.flagcarried) + if(!autocvar_g_ctf_portalteleport) + { ctf_Handle_Throw(self, world, DROP_NORMAL); } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey) +{ + if(MUTATOR_RETURNVALUE || gameover) { return FALSE; } + + entity player = self; + + if((time > player.throw_antispam) && (player.deadflag == DEAD_NO) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch)) + { + // pass the flag to a team mate + if(autocvar_g_ctf_pass) + { + entity head, closest_target; + head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, TRUE); + + while(head) // find the closest acceptable target to pass to + { + if(head.classname == "player" && head.deadflag == DEAD_NO) + if(head != player && !IsDifferentTeam(head, player)) + if(!head.speedrunning && !head.vehicle) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head)); + vector passer_center = CENTER_OR_VIEWOFS(player); + + if(ctf_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest)) + { + if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) + { + if(clienttype(head) == CLIENTTYPE_BOT) + { + centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); + ctf_Handle_Throw(head, player, DROP_PASS); + } + else + { + centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); + centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); + } + player.throw_antispam = time + autocvar_g_ctf_pass_wait; + return TRUE; + } + else if(player.flagcarried) + { + if(closest_target) + { + vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target)); + if(vlen(passer_center - head_center) < vlen(passer_center - closest_target_center)) + { closest_target = head; } + } + else { closest_target = head; } + } + } + } + head = head.chain; + } + + if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return TRUE; } + } + + // throw the flag in front of you + if(autocvar_g_ctf_throw && player.flagcarried) + { + if(player.throw_count == -1) + { + if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) + { + player.throw_prevtime = time; + player.throw_count = 1; + ctf_Handle_Throw(player, world, DROP_THROW); + return TRUE; + } + else + { + centerprint(player, strcat("Too many flag throws, throwing disabled for ", ftos(rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time)), " seconds.")); + return FALSE; + } + } + else + { + if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_time) { player.throw_count = 1; } + else { player.throw_count += 1; } + if(player.throw_count >= autocvar_g_ctf_throw_punish_count) { player.throw_count = -1; } + + player.throw_prevtime = time; + ctf_Handle_Throw(player, world, DROP_THROW); + return TRUE; + } + } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_HelpMePing) +{ + if(self.wps_flagcarrier) // update the flagcarrier waypointsprite with "NEEDING HELP" notification + { + self.wps_helpme_time = time; + WaypointSprite_HelpMePing(self.wps_flagcarrier); + } + else // create a normal help me waypointsprite + { + WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, FALSE, RADARICON_HELPME, '1 0.5 0'); + WaypointSprite_Ping(self.wps_helpme); + } + + return TRUE; +} + +MUTATOR_HOOKFUNCTION(ctf_VehicleEnter) +{ + if(vh_player.flagcarried) + { + if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch) + { + ctf_Handle_Throw(vh_player, world, DROP_NORMAL); + } + else + { + setattachment(vh_player.flagcarried, vh_vehicle, ""); + setorigin(vh_player.flagcarried, VEHICLE_FLAG_OFFSET); + vh_player.flagcarried.scale = VEHICLE_FLAG_SCALE; + //vh_player.flagcarried.angles = '0 0 0'; + } + return TRUE; + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_VehicleExit) +{ + if(vh_player.flagcarried) + { + setattachment(vh_player.flagcarried, vh_player, ""); + setorigin(vh_player.flagcarried, FLAG_CARRY_OFFSET); + vh_player.flagcarried.scale = FLAG_SCALE; + vh_player.flagcarried.angles = '0 0 0'; + return TRUE; + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun) +{ + if(self.flagcarried) + { + bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n"); + ctf_RespawnFlag(self); + return TRUE; + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_MatchEnd) +{ + entity flag; // temporary entity for the search method + + for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + switch(flag.ctf_status) + { + case FLAG_DROPPED: + case FLAG_PASSING: + { + // lock the flag, game is over + flag.movetype = MOVETYPE_NONE; + flag.takedamage = DAMAGE_NO; + flag.solid = SOLID_NOT; + flag.nextthink = FALSE; // stop thinking + + print("stopping the ", flag.netname, " from moving.\n"); + break; + } + + default: + case FLAG_BASE: + case FLAG_CARRY: + { + // do nothing for these flags + break; + } + } + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(ctf_BotRoles) +{ + havocbot_ctf_reset_role(self); + return TRUE; +} + + +// ========== +// Spawnfuncs +// ========== + +/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in team one (Red). +Keys: "angle" viewing angle when spawning. */ +void spawnfunc_info_player_team1() +{ + if(g_assault) { remove(self); return; } + + self.team = COLOR_TEAM1; // red + spawnfunc_info_player_deathmatch(); +} + + +/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in team two (Blue). +Keys: "angle" viewing angle when spawning. */ +void spawnfunc_info_player_team2() +{ + if(g_assault) { remove(self); return; } + + self.team = COLOR_TEAM2; // blue + spawnfunc_info_player_deathmatch(); +} + +/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in team three (Yellow). +Keys: "angle" viewing angle when spawning. */ +void spawnfunc_info_player_team3() +{ + if(g_assault) { remove(self); return; } + + self.team = COLOR_TEAM3; // yellow + spawnfunc_info_player_deathmatch(); +} + + +/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24) +CTF Starting point for a player in team four (Purple). +Keys: "angle" viewing angle when spawning. */ +void spawnfunc_info_player_team4() +{ + if(g_assault) { remove(self); return; } + + self.team = COLOR_TEAM4; // purple + spawnfunc_info_player_deathmatch(); +} + +/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team one (Red). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red and blue as skins 0 and 1... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +void spawnfunc_item_flag_team1() +{ + if(!g_ctf) { remove(self); return; } + + ctf_FlagSetup(1, self); // 1 = red +} + +/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team two (Blue). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red and blue as skins 0 and 1... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +void spawnfunc_item_flag_team2() +{ + if(!g_ctf) { remove(self); return; } + + ctf_FlagSetup(0, self); // the 0 is misleading, but -- 0 = blue. +} + +/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32) +Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map. +Note: If you use spawnfunc_ctf_team entities you must define at least 2! However, unlike domination, you don't need to make a blank one too. +Keys: +"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)... +"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ +void spawnfunc_ctf_team() +{ + if(!g_ctf) { remove(self); return; } + + self.classname = "ctf_team"; + self.team = self.cnt + 1; +} + +// compatibility for quake maps +void spawnfunc_team_CTF_redflag() { spawnfunc_item_flag_team1(); } +void spawnfunc_team_CTF_blueflag() { spawnfunc_item_flag_team2(); } +void spawnfunc_team_CTF_redplayer() { spawnfunc_info_player_team1(); } +void spawnfunc_team_CTF_blueplayer() { spawnfunc_info_player_team2(); } +void spawnfunc_team_CTF_redspawn() { spawnfunc_info_player_team1(); } +void spawnfunc_team_CTF_bluespawn() { spawnfunc_info_player_team2(); } + + +// ============== +// Initialization +// ============== + +// scoreboard setup +void ctf_ScoreRules() +{ + ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, TRUE); + ScoreInfo_SetLabel_TeamScore (ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS, "fckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS, "returns", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER); + ScoreRules_basics_end(); +} + +// code from here on is just to support maps that don't have flag and team entities +void ctf_SpawnTeam (string teamname, float teamcolor) +{ + entity oldself; + oldself = self; + self = spawn(); + self.classname = "ctf_team"; + self.netname = teamname; + self.cnt = teamcolor; + + spawnfunc_ctf_team(); + + self = oldself; +} + +void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to be set up. +{ + // if no teams are found, spawn defaults + if(find(world, classname, "ctf_team") == world) + { + print("No ""ctf_team"" entities found on this map, creating them anyway.\n"); + ctf_SpawnTeam("Red", COLOR_TEAM1 - 1); + ctf_SpawnTeam("Blue", COLOR_TEAM2 - 1); + } + + ctf_ScoreRules(); +} + +void ctf_Initialize() +{ + ctf_captimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"))); + + ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore; + ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio; + ctf_captureshield_force = autocvar_g_ctf_shield_force; + + InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE); +} + + +MUTATOR_DEFINITION(gamemode_ctf) +{ + MUTATOR_HOOK(MakePlayerObserver, ctf_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, ctf_RemovePlayer, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDies, ctf_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(MatchEnd, ctf_MatchEnd, CBC_ORDER_ANY); + MUTATOR_HOOK(PortalTeleport, ctf_PortalTeleport, CBC_ORDER_ANY); + MUTATOR_HOOK(GiveFragsForKill, ctf_GiveFragsForKill, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerPreThink, ctf_PlayerPreThink, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerDamage_Calculate, ctf_PlayerDamage, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerUseKey, ctf_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(HelpMePing, ctf_HelpMePing, CBC_ORDER_ANY); + MUTATOR_HOOK(VehicleEnter, ctf_VehicleEnter, CBC_ORDER_ANY); + MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY); + MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + g_ctf = 1; + ctf_Initialize(); + } + + MUTATOR_ONREMOVE + { + g_ctf = 0; + error("This is a game type and it cannot be removed at runtime."); + } + + return 0; +} diff --git a/qcsrc/server/mutators/gamemode_ctf.qh b/qcsrc/server/mutators/gamemode_ctf.qh new file mode 100644 index 0000000000..b6ca033bb3 --- /dev/null +++ b/qcsrc/server/mutators/gamemode_ctf.qh @@ -0,0 +1,132 @@ +// these are needed since mutators are compiled last + +// used in cheats.qc +void ctf_RespawnFlag(entity flag) + +// score rule declarations +#define ST_CTF_CAPS 1 +#define SP_CTF_CAPS 4 +#define SP_CTF_CAPTIME 5 +#define SP_CTF_PICKUPS 6 +#define SP_CTF_DROPS 7 +#define SP_CTF_FCKILLS 8 +#define SP_CTF_RETURNS 9 + +// flag constants // for most of these, there is just one question to be asked: WHYYYYY? +#define FLAG_MIN (PL_MIN + '0 0 -13') +#define FLAG_MAX (PL_MAX + '0 0 -13') + +#define FLAG_SCALE 0.6 + +#define FLAG_THINKRATE 0.2 +#define FLAG_TOUCHRATE 0.5 +#define WPFE_THINKRATE 0.5 + +#define FLAG_DROP_OFFSET ('0 0 32') +#define FLAG_CARRY_OFFSET ('-16 0 8') +#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13)) +#define FLAG_WAYPOINT_OFFSET ('0 0 64') +#define FLAG_FLOAT_OFFSET ('0 0 32') +#define FLAG_PASS_ARC_OFFSET ('0 0 -10') + +#define VEHICLE_FLAG_OFFSET ('0 0 96') +#define VEHICLE_FLAG_SCALE 1.0 + +// waypoint colors +#define WPCOLOR_ENEMYFC(t) (colormapPaletteColor(t - 1, FALSE) * 0.75) +#define WPCOLOR_FLAGCARRIER(t) ('0.8 0.8 0') +#define WPCOLOR_DROPPEDFLAG(t) (('0.25 0.25 0.25' + colormapPaletteColor(t - 1, FALSE)) * 0.5) + +// sounds +#define snd_flag_taken noise +#define snd_flag_returned noise1 +#define snd_flag_capture noise2 +#define snd_flag_respawn noise3 +.string snd_flag_dropped; +.string snd_flag_touch; +.string snd_flag_pass; + +// effects +.string toucheffect; +.string passeffect; +.string capeffect; + +// list of flags on the map +entity ctf_worldflaglist; +.entity ctf_worldflagnext; +.entity ctf_staleflagnext; + +// waypoint sprites +.entity bot_basewaypoint; // flag waypointsprite +.entity wps_helpme; +.entity wps_flagbase; +.entity wps_flagcarrier; +.entity wps_flagdropped; +.entity wps_enemyflagcarrier; +.float wps_helpme_time; +float wpforenemy_announced; +float wpforenemy_nextthink; + +// statuses +#define FLAG_BASE 1 +#define FLAG_DROPPED 2 +#define FLAG_CARRY 3 +#define FLAG_PASSING 4 + +#define DROP_NORMAL 1 +#define DROP_THROW 2 +#define DROP_PASS 3 +#define DROP_RESET 4 + +#define PICKUP_BASE 1 +#define PICKUP_DROPPED 2 + +#define CAPTURE_NORMAL 1 +#define CAPTURE_DROPPED 2 + +#define RETURN_TIMEOUT 1 +#define RETURN_DROPPED 2 +#define RETURN_DAMAGE 3 +#define RETURN_SPEEDRUN 4 +#define RETURN_NEEDKILL 5 + +// flag properties +#define ctf_spawnorigin dropped_origin +float ctf_stalemate; // indicates that a stalemate is active +float ctf_captimerecord; // record time for capturing the flag +.float ctf_pickuptime; +.float ctf_droptime; +.float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally) +.entity ctf_dropper; // don't allow spam of dropping the flag +.float max_flag_health; +.float next_take_time; + +// passing/throwing properties +.float pass_distance; +.entity pass_sender; +.entity pass_target; +.float throw_antispam; +.float throw_prevtime; +.float throw_count; + +// CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag. +.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture +float ctf_captureshield_min_negscore; // punish at -20 points +float ctf_captureshield_max_ratio; // punish at most 30% of each team +float ctf_captureshield_force; // push force of the shield + +// bot player logic +#define HAVOCBOT_CTF_ROLE_NONE 0 +#define HAVOCBOT_CTF_ROLE_DEFENSE 2 +#define HAVOCBOT_CTF_ROLE_MIDDLE 4 +#define HAVOCBOT_CTF_ROLE_OFFENSE 8 +#define HAVOCBOT_CTF_ROLE_CARRIER 16 +#define HAVOCBOT_CTF_ROLE_RETRIEVER 32 +#define HAVOCBOT_CTF_ROLE_ESCORT 64 + +.float havocbot_cantfindflag; + +vector havocbot_ctf_middlepoint; +float havocbot_ctf_middlepoint_radius; + +void havocbot_role_ctf_setrole(entity bot, float role); diff --git a/qcsrc/server/mutators/gamemode_freezetag.qc b/qcsrc/server/mutators/gamemode_freezetag.qc index fa5a365ba4..aedfd6364c 100644 --- a/qcsrc/server/mutators/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/gamemode_freezetag.qc @@ -27,7 +27,7 @@ void freezetag_CheckWinner() FOR_EACH_PLAYER(e) { - if(e.freezetag_frozen == 0 && e.classname == "player" && e.health >= 1) // here's one player from the winning team... good + if(e.freezetag_frozen == 0 && e.health >= 1) // here's one player from the winning team... good { winner = e; break; // break, we found the winner @@ -129,9 +129,118 @@ void freezetag_Unfreeze(entity attacker) WaypointSprite_Kill(self.waypointsprite_attached); } + +// ================ +// Bot player logic +// ================ + +void() havocbot_role_ft_freeing; +void() havocbot_role_ft_offense; + +void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradius) +{ + entity head; + float distance; + + FOR_EACH_PLAYER(head) + { + if ((head != self) && (head.team == self.team)) + { + if (head.freezetag_frozen) + { + distance = vlen(head.origin - org); + if (distance > sradius) + continue; + navigation_routerating(head, ratingscale, 2000); + } + else + { + // If teamate is not frozen still seek them out as fight better + // in a group. + navigation_routerating(head, ratingscale/3, 2000); + } + } + } +} + +void havocbot_role_ft_offense() +{ + entity head; + float unfrozen; + + if(self.deadflag != DEAD_NO) + return; + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + + // Count how many players on team are unfrozen. + unfrozen = 0; + FOR_EACH_PLAYER(head) + { + if ((head.team == self.team) && (!head.freezetag_frozen)) + unfrozen++; + } + + // If only one left on team or if role has timed out then start trying to free players. + if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout)) + { + dprint("changing role to freeing\n"); + self.havocbot_role = havocbot_role_ft_freeing; + self.havocbot_role_timeout = 0; + return; + } + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + havocbot_goalrating_freeplayers(9000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } +} + +void havocbot_role_ft_freeing() +{ + if(self.deadflag != DEAD_NO) + return; + + if (!self.havocbot_role_timeout) + self.havocbot_role_timeout = time + random() * 10 + 20; + + if (time > self.havocbot_role_timeout) + { + dprint("changing role to offense\n"); + self.havocbot_role = havocbot_role_ft_offense; + self.havocbot_role_timeout = 0; + return; + } + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(8000, self.origin, 10000); + havocbot_goalrating_enemyplayers(10000, self.origin, 10000); + havocbot_goalrating_freeplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } +} + + +// ============== +// Hook Functions +// ============== + MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer) { - if(self.freezetag_frozen == 0) + if(self.freezetag_frozen == 0 && self.health >= 1) { if(self.team == COLOR_TEAM1) --redalive; @@ -166,22 +275,21 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies) --pinkalive; --totalalive; - freezetag_Freeze(frag_attacker); + freezetag_Freeze(frag_attacker); } - if(frag_attacker.classname == STR_PLAYER) - centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n")); - if(frag_attacker == frag_target || frag_attacker == world) { - if(frag_target.classname == STR_PLAYER) - centerprint(frag_target, "^1You froze yourself.\n"); + if(frag_target.classname == STR_PLAYER) + centerprint(frag_target, "^1You froze yourself.\n"); bprint("^7", frag_target.netname, "^1 froze himself.\n"); } else { - if(frag_target.classname == STR_PLAYER) - centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n")); + if(frag_target.classname == STR_PLAYER) + centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n")); + if(frag_attacker.classname == STR_PLAYER) + centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n")); bprint("^7", frag_target.netname, "^1 was frozen by ^7", frag_attacker.netname, ".\n"); } @@ -194,7 +302,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies) MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn) { - freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed + freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed if(total_players == 1 && time > game_starttime) // only one player active on server, start a new match immediately if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round @@ -335,6 +443,19 @@ MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon) return 0; } +MUTATOR_HOOKFUNCTION(freezetag_BotRoles) +{ + if not(self.deadflag) + { + if (random() < 0.5) + self.havocbot_role = havocbot_role_ft_freeing; + else + self.havocbot_role = havocbot_role_ft_offense; + } + + return TRUE; +} + MUTATOR_DEFINITION(gamemode_freezetag) { MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY); @@ -346,18 +467,17 @@ MUTATOR_DEFINITION(gamemode_freezetag) MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST); MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY); MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_FIRST); //first, last or any? dunno. + MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY); MUTATOR_ONADD { if(time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); - g_freezetag = 1; freezetag_Initialize(); } MUTATOR_ONREMOVE { - g_freezetag = 0; error("This is a game type and it cannot be removed at runtime."); } diff --git a/qcsrc/server/mutators/gamemode_keepaway.qc b/qcsrc/server/mutators/gamemode_keepaway.qc index 9134353e01..07c96671c6 100644 --- a/qcsrc/server/mutators/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/gamemode_keepaway.qc @@ -1,61 +1,23 @@ -void ka_SpawnBall(void); -void ka_TouchEvent(void); -void ka_RespawnBall(void); -void ka_DropEvent(entity); -void ka_TimeScoring(void); -void ka_EventLog(string, entity); +// =========================================================== +// Keepaway game mode coding, written by Samual and Diabolik +// Last updated: September, 2012 +// =========================================================== -entity ka_ball; - -float ka_ballcarrier_waypointsprite_visible_for_player(entity); - -void ka_Initialize() // run at the start of a match, initiates game mode +float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame { - if(!g_keepaway) - return; + if(e.ballcarried) + if(other.classname == "spectator") + return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen - precache_sound("keepaway/pickedup.wav"); - precache_sound("keepaway/dropped.wav"); - precache_sound("keepaway/respawn.wav"); - precache_sound("keepaway/touch.wav"); - - ScoreRules_keepaway(); - ka_SpawnBall(); -} - -void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal -{ - if(self.owner) - if(self.owner.classname == "player") - ka_DropEvent(self.owner); + // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup - ka_RespawnBall(); + return TRUE; } -void ka_SpawnBall() // loads various values for the ball, runs only once at start of match +void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later { - if(!g_keepaway) { return; } - - entity e; - e = spawn(); - e.model = "models/orbs/orbblue.md3"; - precache_model(e.model); - setmodel(e, e.model); - setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off - e.classname = "keepawayball"; - e.damageforcescale = autocvar_g_keepawayball_damageforcescale; - e.takedamage = DAMAGE_YES; - e.solid = SOLID_TRIGGER; - e.movetype = MOVETYPE_BOUNCE; - e.glow_color = autocvar_g_keepawayball_trail_color; - e.glow_trail = TRUE; - e.flags = FL_ITEM; - e.reset = ka_Reset; - e.touch = ka_TouchEvent; - e.owner = world; - ka_ball = e; - - InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); } void ka_RespawnBall() // runs whenever the ball needs to be relocated @@ -87,6 +49,18 @@ void ka_RespawnBall() // runs whenever the ball needs to be relocated } } +void ka_TimeScoring() +{ + if(self.owner.ballcarried) + { // add points for holding the ball after a certain amount of time + if(autocvar_g_keepaway_score_timepoints) + PlayerScore_Add(self.owner, SP_SCORE, autocvar_g_keepaway_score_timepoints); + + PlayerScore_Add(self.owner, SP_KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds" + self.nextthink = time + autocvar_g_keepaway_score_timeinterval; + } +} + void ka_TouchEvent() // runs any time that the ball comes in contact with something { if(gameover) { return; } @@ -185,35 +159,90 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier); } -float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame +void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal { - if(e.ballcarried) - if(other.classname == "spectator") - return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen - - // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup + if((self.owner) && (self.owner.classname == "player")) + ka_DropEvent(self.owner); - return TRUE; + ka_RespawnBall(); } -void ka_TimeScoring() + +// ================ +// Bot player logic +// ================ + +void havocbot_goalrating_ball(float ratingscale, vector org) { - if(self.owner.ballcarried) - { // add points for holding the ball after a certain amount of time - if(autocvar_g_keepaway_score_timepoints) - PlayerScore_Add(self.owner, SP_SCORE, autocvar_g_keepaway_score_timepoints); - - PlayerScore_Add(self.owner, SP_KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds" - self.nextthink = time + autocvar_g_keepaway_score_timeinterval; + float t; + entity ball_owner; + ball_owner = ka_ball.owner; + + if (ball_owner == self) + return; + + // If ball is carried by player then hunt them down. + if (ball_owner) + { + t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); + navigation_routerating(ball_owner, t * ratingscale, 2000); } + + // Ball has been dropped so collect. + navigation_routerating(ka_ball, ratingscale, 2000); } -void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later +void havocbot_role_ka_carrier() { - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } + + if (!self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_collector; + self.bot_strategytime = 0; + } +} + +void havocbot_role_ka_collector() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(1000, self.origin, 10000); + havocbot_goalrating_ball(20000, self.origin); + navigation_goalrating_end(); + } + + if (self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_carrier; + self.bot_strategytime = 0; + } } + +// ============== +// Hook Functions +// ============== + MUTATOR_HOOKFUNCTION(ka_Scoring) { if((frag_attacker != frag_target) && (frag_attacker.classname == "player")) @@ -314,6 +343,70 @@ MUTATOR_HOOKFUNCTION(ka_PlayerPowerups) return 0; } +MUTATOR_HOOKFUNCTION(ka_BotRoles) +{ + if (self.ballcarried) + self.havocbot_role = havocbot_role_ka_carrier; + else + self.havocbot_role = havocbot_role_ka_collector; + return TRUE; +} + + +// ============== +// Initialization +// ============== + +void ka_SpawnBall() // loads various values for the ball, runs only once at start of match +{ + if(!g_keepaway) { return; } + + entity e; + e = spawn(); + e.model = "models/orbs/orbblue.md3"; + precache_model(e.model); + setmodel(e, e.model); + setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off + e.classname = "keepawayball"; + e.damageforcescale = autocvar_g_keepawayball_damageforcescale; + e.takedamage = DAMAGE_YES; + e.solid = SOLID_TRIGGER; + e.movetype = MOVETYPE_BOUNCE; + e.glow_color = autocvar_g_keepawayball_trail_color; + e.glow_trail = TRUE; + e.flags = FL_ITEM; + e.reset = ka_Reset; + e.touch = ka_TouchEvent; + e.owner = world; + ka_ball = e; + + InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. +} + +void ka_ScoreRules() +{ + ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, TRUE); // SFL_SORT_PRIO_PRIMARY + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); + ScoreRules_basics_end(); +} + +void ka_Initialize() // run at the start of a match, initiates game mode +{ + if(!g_keepaway) + return; + + precache_sound("keepaway/pickedup.wav"); + precache_sound("keepaway/dropped.wav"); + precache_sound("keepaway/respawn.wav"); + precache_sound("keepaway/touch.wav"); + + ka_ScoreRules(); + ka_SpawnBall(); +} + + MUTATOR_DEFINITION(gamemode_keepaway) { MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY); @@ -324,18 +417,17 @@ MUTATOR_DEFINITION(gamemode_keepaway) MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerUseKey, ka_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, ka_BotRoles, CBC_ORDER_ANY); MUTATOR_ONADD { if(time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); - g_keepaway = 1; ka_Initialize(); } MUTATOR_ONREMOVE { - g_keepaway = 0; error("This is a game type and it cannot be removed at runtime."); } diff --git a/qcsrc/server/mutators/gamemode_keepaway.qh b/qcsrc/server/mutators/gamemode_keepaway.qh new file mode 100644 index 0000000000..062fc9e0cb --- /dev/null +++ b/qcsrc/server/mutators/gamemode_keepaway.qh @@ -0,0 +1,10 @@ +// these are needed since mutators are compiled last + +entity ka_ball; + +#define SP_KEEPAWAY_PICKUPS 4 +#define SP_KEEPAWAY_CARRIERKILLS 5 +#define SP_KEEPAWAY_BCTIME 6 + +void() havocbot_role_ka_carrier; +void() havocbot_role_ka_collector; diff --git a/qcsrc/server/mutators/gamemode_keyhunt.qc b/qcsrc/server/mutators/gamemode_keyhunt.qc index d3f4fdc87e..58d732d7f7 100644 --- a/qcsrc/server/mutators/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/gamemode_keyhunt.qc @@ -32,11 +32,11 @@ float kh_no_radar_circles; .float siren_time; // time delay the siren //.float stuff_time; // time delay to stuffcmd a cvar -float test[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; -//test[0] = status of dropped keys, test[1 - 16] = player # +float kh_keystatus[17]; +//kh_keystatus[0] = status of dropped keys, kh_keystatus[1 - 16] = player # //replace 17 with cvar("maxplayers") or similar !!!!!!!!! //for(i = 0; i < maxplayers; ++i) -// test[i] = "0"; +// kh_keystatus[i] = "0"; float kh_Team_ByID(float t) { @@ -342,7 +342,7 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic key.kh_next.kh_prev = key; float i; - i = test[key.owner.playerid]; + i = kh_keystatus[key.owner.playerid]; if(key.netname == "^1red key") i += 1; if(key.netname == "^4blue key") @@ -351,7 +351,7 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic i += 4; if(key.netname == "^6pink key") i += 8; - test[key.owner.playerid] = i; + kh_keystatus[key.owner.playerid] = i; kh_Key_Attach(key); @@ -412,6 +412,13 @@ void kh_Key_Damage(entity inflictor, entity attacker, float damage, float deatht { if(self.owner) return; + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + // touching lava, or hurt trigger + // what shall we do? + // immediately return is bad + // maybe start a shorter countdown? + } if(vlen(force) <= 0) return; if(time > self.pushltime) @@ -441,6 +448,15 @@ void kh_Key_Touch() // runs many, many times when a key has been dropped and ca if(self.owner) // already carried return; + + if(ITEM_TOUCH_NEEDKILL()) + { + // touching sky, or nodrop + // what shall we do? + // immediately return is bad + // maybe start a shorter countdown? + } + if(other.classname != "player") return; if(other.deadflag != DEAD_NO) @@ -529,6 +545,8 @@ void kh_WinnerTeam(float teem) // runs when a team wins first = TRUE; midpoint = '0 0 0'; + firstorigin = '0 0 0'; + lastorigin = '0 0 0'; FOR_EACH_KH_KEY(key) { vector thisorigin; @@ -919,7 +937,7 @@ void kh_StartRound() // runs at the start of each round { teem = kh_Team_ByID(i); players = 0; - entity my_player; + entity my_player = world; FOR_EACH_PLAYER(player) if(player.deadflag == DEAD_NO) if(!player.BUTTON_CHAT) @@ -1090,13 +1108,11 @@ MUTATOR_DEFINITION(gamemode_keyhunt) { if(time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); - g_keyhunt = 1; kh_Initialize(); } MUTATOR_ONREMOVE { - g_keyhunt = 0; error("This is a game type and it cannot be removed at runtime."); } diff --git a/qcsrc/server/mutators/gamemode_nexball.qc b/qcsrc/server/mutators/gamemode_nexball.qc index 8554a12238..808d1a29b3 100644 --- a/qcsrc/server/mutators/gamemode_nexball.qc +++ b/qcsrc/server/mutators/gamemode_nexball.qc @@ -117,7 +117,7 @@ void GiveBall(entity plyr, entity ball) ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it ball.team = plyr.team; plyr.ballcarried = ball; - ball.dropperid = plyr.playerid; + ball.nb_dropper = plyr; plyr.effects |= autocvar_g_nexball_basketball_effects_default; ball.effects &~= autocvar_g_nexball_basketball_effects_default; @@ -137,15 +137,15 @@ void GiveBall(entity plyr, entity ball) ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold; } - ownr = self; - self = plyr; - self.weaponentity.weapons = self.weapons; - self.weaponentity.switchweapon = self.weapon; - self.weapons = W_WeaponBit(WEP_PORTO); - weapon_action(WEP_PORTO, WR_RESETPLAYER); - self.switchweapon = WEP_PORTO; - W_SwitchWeapon(WEP_PORTO); - self = ownr; + ownr = self; + self = plyr; + WEPSET_COPY_EE(self.weaponentity, self); + self.weaponentity.switchweapon = self.weapon; + WEPSET_COPY_EW(self, WEP_PORTO); + weapon_action(WEP_PORTO, WR_RESETPLAYER); + self.switchweapon = WEP_PORTO; + W_SwitchWeapon(WEP_PORTO); + self = ownr; } void DropBall(entity ball, vector org, vector vel) @@ -160,7 +160,7 @@ void DropBall(entity ball, vector org, vector vel) ball.flags &~= FL_ONGROUND; ball.scale = ball_scale; ball.velocity = vel; - ball.ctf_droptime = time; + ball.nb_droptime = time; ball.touch = basketball_touch; ball.think = ResetBall; ball.nextthink = min(time + autocvar_g_nexball_delay_idle, ball.teamtime); @@ -201,7 +201,7 @@ void InitBall(void) void ResetBall(void) { - if(self.cnt < 2) // step 1 + if(self.cnt < 2) // step 1 { if(time == self.teamtime) bprint("The ", ColoredTeamName(self.team), " held the ball for too long.\n"); @@ -213,14 +213,14 @@ void ResetBall(void) self.cnt = 2; self.nextthink = time; } - else if(self.cnt < 4) // step 2 and 3 + else if(self.cnt < 4) // step 2 and 3 { // dprint("Step ", ftos(self.cnt), ": Calculated velocity: ", vtos(self.spawnorigin - self.origin), ", time: ", ftos(time), "\n"); self.velocity = (self.spawnorigin - self.origin) * (self.cnt - 1); // 1 or 0.5 second movement self.nextthink = time + 0.5; self.cnt += 1; } - else // step 4 + else // step 4 { // dprint("Step 4: time: ", ftos(time), "\n"); if(vlen(self.origin - self.spawnorigin) > 10) // should not happen anymore @@ -257,22 +257,22 @@ void football_touch(void) self.pusher = other; self.team = other.team; - if(autocvar_g_nexball_football_physics == -1) // MrBougo try 1, before decompiling Rev's original + if(autocvar_g_nexball_football_physics == -1) // MrBougo try 1, before decompiling Rev's original { if(vlen(other.velocity)) self.velocity = other.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up; } - else if(autocvar_g_nexball_football_physics == 1) // MrBougo's modded Rev style: partially independant of the height of the aiming point + else if(autocvar_g_nexball_football_physics == 1) // MrBougo's modded Rev style: partially independant of the height of the aiming point { makevectors(other.v_angle); self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up; } - else if(autocvar_g_nexball_football_physics == 2) // 2nd mod try: totally independant. Really playable! + else if(autocvar_g_nexball_football_physics == 2) // 2nd mod try: totally independant. Really playable! { makevectors(other.v_angle_y * '0 1 0'); self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up; } - else // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant) + else // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant) { makevectors(other.v_angle); self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up; @@ -287,7 +287,7 @@ void basketball_touch(void) football_touch(); return; } - if(!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_nexball_delay_collect)) + if(!self.cnt && other.classname == "player" && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect)) { if(other.health <= 0) return; @@ -323,13 +323,15 @@ void GoalTouch(void) if(nb_teams == 2) otherteam = OtherTeam(ball.team); + else + otherteam = 0; if((isclient = ball.pusher.flags & FL_CLIENT)) pname = ball.pusher.netname; 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"); @@ -353,7 +355,7 @@ void GoalTouch(void) bprint("The ball was returned.\n"); pscore = 0; } - else //score + else //score { LogNB(strcat("goal:", ftos(self.team)), ball.pusher); bprint("Goaaaaal! ", pname, "^7 scored a point for the ", ColoredTeamName(ball.team), ".\n"); @@ -390,7 +392,7 @@ void GoalTouch(void) } //=======================// -// team ents // +// team ents // //=======================// void spawnfunc_nexball_team(void) { @@ -416,7 +418,7 @@ void nb_spawnteam(string teamname, float teamcolor) void nb_spawnteams(void) { - float t_r, t_b, t_y, t_p; + float t_r = 0, t_b = 0, t_y = 0, t_p = 0; entity e; for(e = world; (e = find(e, classname, "nexball_goal"));) { @@ -463,7 +465,7 @@ void nb_delayedinit(void) //=======================// -// spawnfuncs // +// spawnfuncs // //=======================// void SpawnBall(void) @@ -524,7 +526,7 @@ void SpawnBall(void) void spawnfunc_nexball_basketball(void) { - nexball_mode |= NBM_BASKETBALL; + nexball_mode |= NBM_BASKETBALL; self.classname = "nexball_basketball"; if not(balls & BALL_BASKET) { @@ -547,7 +549,7 @@ void spawnfunc_nexball_basketball(void) void spawnfunc_nexball_football(void) { - nexball_mode |= NBM_FOOTBALL; + nexball_mode |= NBM_FOOTBALL; self.classname = "nexball_football"; self.solid = SOLID_TRIGGER; balls |= BALL_FOOT; @@ -627,11 +629,11 @@ void spawnfunc_ball_basketball(void) // The "red goal" is defended by blue team. A ball in there counts as a point for red. void spawnfunc_ball_redgoal(void) { - spawnfunc_nexball_bluegoal(); // I blame Revenant + spawnfunc_nexball_bluegoal(); // I blame Revenant } void spawnfunc_ball_bluegoal(void) { - spawnfunc_nexball_redgoal(); // but he didn't mean to cause trouble :p + spawnfunc_nexball_redgoal(); // but he didn't mean to cause trouble :p } void spawnfunc_ball_fault(void) { @@ -643,32 +645,32 @@ void spawnfunc_ball_bound(void) } //=======================// -// Weapon code // +// Weapon code // //=======================// void W_Nexball_Think() { - //dprint("W_Nexball_Think\n"); - //vector new_dir = steerlib_arrive(self.enemy.origin, 2500); - vector new_dir = normalize(self.enemy.origin - self.origin); - vector old_dir = normalize(self.velocity); - float _speed = vlen(self.velocity); - vector new_vel = normalize(old_dir + (new_dir * autocvar_g_nexball_safepass_turnrate)) * _speed; - //vector new_vel = (new_dir * autocvar_g_nexball_safepass_turnrate - - self.velocity = new_vel; - - self.nextthink = time; + //dprint("W_Nexball_Think\n"); + //vector new_dir = steerlib_arrive(self.enemy.origin, 2500); + vector new_dir = normalize(self.enemy.origin + '0 0 50' - self.origin); + vector old_dir = normalize(self.velocity); + float _speed = vlen(self.velocity); + vector new_vel = normalize(old_dir + (new_dir * autocvar_g_nexball_safepass_turnrate)) * _speed; + //vector new_vel = (new_dir * autocvar_g_nexball_safepass_turnrate + + self.velocity = new_vel; + + self.nextthink = time; } void W_Nexball_Touch(void) { entity ball, attacker; attacker = self.owner; - //self.think = SUB_Null; - //self.enemy = world; - + //self.think = SUB_Null; + //self.enemy = world; + PROJECTILE_TOUCH; if(attacker.team != other.team || autocvar_g_nexball_basketball_teamsteal) if((ball = other.ballcarried) && (attacker.classname == "player")) @@ -723,7 +725,7 @@ void W_Nexball_Attack(float t) mul = mi + (ma - mi) * mul; // range from the minimal power to the maximal power } - DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(self.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, FALSE)); + DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(self.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, FALSE)); //TODO: use the speed_up cvar too ?? @@ -733,16 +735,16 @@ void W_Nexball_Attack2(void) { if(self.ballcarried.enemy) { - entity _ball = self.ballcarried; - W_SetupShot(self, FALSE, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0); - DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32)); - _ball.think = W_Nexball_Think; - _ball.nextthink = time; - return; + entity _ball = self.ballcarried; + W_SetupShot(self, FALSE, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0); + DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32)); + _ball.think = W_Nexball_Think; + _ball.nextthink = time; + return; } - - if(!autocvar_g_nexball_tackling) - return; + + if(!autocvar_g_nexball_tackling) + return; entity missile; if(!(balls & BALL_BASKET)) @@ -774,29 +776,29 @@ void W_Nexball_Attack2(void) var const float() nullfunc; float ball_customize() { - if(!self.owner) - { - self.effects &~= EF_FLAME; - self.scale = 1; - self.customizeentityforclient = nullfunc; - return TRUE; - } - - if(other == self.owner) - { - self.scale = autocvar_g_nexball_viewmodel_scale; - if(self.enemy) - self.effects |= EF_FLAME; - else - self.effects &~= EF_FLAME; - } - else - { - self.effects &~= EF_FLAME; - self.scale = 1; - } - - return TRUE; + if(!self.owner) + { + self.effects &~= EF_FLAME; + self.scale = 1; + self.customizeentityforclient = nullfunc; + return TRUE; + } + + if(other == self.owner) + { + self.scale = autocvar_g_nexball_viewmodel_scale; + if(self.enemy) + self.effects |= EF_FLAME; + else + self.effects &~= EF_FLAME; + } + else + { + self.effects &~= EF_FLAME; + self.scale = 1; + } + + return TRUE; } float w_nexball_weapon(float req) @@ -877,75 +879,75 @@ MUTATOR_HOOKFUNCTION(nexball_BuildMutatorsPrettyString) MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink) { - makevectors(self.v_angle); - if(nexball_mode & NBM_BASKETBALL) - { - if(self.ballcarried) - { - // 'view ball' - self.ballcarried.velocity = self.velocity; - self.ballcarried.customizeentityforclient = ball_customize; - - setorigin(self.ballcarried, self.origin + self.view_ofs + - v_forward * autocvar_g_nexball_viewmodel_offset_x + - v_right * autocvar_g_nexball_viewmodel_offset_y + - v_up * autocvar_g_nexball_viewmodel_offset_z); - - // 'safe passing' - if(autocvar_g_nexball_safepass_maxdist) - { - if(self.ballcarried.wait < time && self.ballcarried.enemy) - { - //centerprint(self, sprintf("Lost lock on %s", self.ballcarried.enemy.netname)); - self.ballcarried.enemy = world; - } - - - //tracebox(self.origin + self.view_ofs, '-2 -2 -2', '2 2 2', self.origin + self.view_ofs + v_forward * autocvar_g_nexball_safepass_maxdist); - crosshair_trace(self); - if( trace_ent && - trace_ent.flags & FL_CLIENT && - trace_ent.deadflag == DEAD_NO && - trace_ent.team == self.team && - vlen(trace_ent.origin - self.origin) <= autocvar_g_nexball_safepass_maxdist ) - { - - //if(self.ballcarried.enemy != trace_ent) - // centerprint(self, sprintf("Locked to %s", trace_ent.netname)); - self.ballcarried.enemy = trace_ent; - self.ballcarried.wait = time + autocvar_g_nexball_safepass_holdtime; - - - } - } - } - else - { - if(self.weaponentity.weapons) - { - self.weapons = self.weaponentity.weapons; - weapon_action(WEP_PORTO, WR_RESETPLAYER); - self.switchweapon = self.weaponentity.switchweapon; - W_SwitchWeapon(self.switchweapon); - - self.weaponentity.weapons = 0; - } - } - - } - return FALSE; + 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; } MUTATOR_HOOKFUNCTION(nexball_PlayerSpawn) -{ - self.weaponentity.weapons = 0; - - if(nexball_mode & NBM_BASKETBALL) - self.weapons |= W_WeaponBit(WEP_PORTO); - else - self.weapons = 0; // W_WeaponBit(WEP_PORTO); +{ + 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) @@ -960,7 +962,6 @@ MUTATOR_DEFINITION(gamemode_nexball) MUTATOR_ONADD { - g_nexball = 1; g_nexball_meter_period = autocvar_g_nexball_meter_period; if(g_nexball_meter_period <= 0) g_nexball_meter_period = 2; // avoid division by zero etc. due to silly users @@ -970,9 +971,9 @@ MUTATOR_DEFINITION(gamemode_nexball) // General settings /* CVTOV(g_nexball_football_boost_forward); //100 - CVTOV(g_nexball_football_boost_up); //200 - CVTOV(g_nexball_delay_idle); //10 - CVTOV(g_nexball_football_physics); //0 + CVTOV(g_nexball_football_boost_up); //200 + CVTOV(g_nexball_delay_idle); //10 + CVTOV(g_nexball_football_physics); //0 */ radar_showennemies = autocvar_g_nexball_radar_showallplayers; diff --git a/qcsrc/server/mutators/gamemode_nexball.qh b/qcsrc/server/mutators/gamemode_nexball.qh index 545ec96d75..4b0b7eaa15 100644 --- a/qcsrc/server/mutators/gamemode_nexball.qh +++ b/qcsrc/server/mutators/gamemode_nexball.qh @@ -27,4 +27,7 @@ float balls; float ball_scale; float nb_teams; -.float teamtime; \ No newline at end of file +.entity nb_dropper; +.float nb_droptime; + +.float teamtime; diff --git a/qcsrc/server/mutators/gamemode_onslaught.qc b/qcsrc/server/mutators/gamemode_onslaught.qc new file mode 100644 index 0000000000..060447c39e --- /dev/null +++ b/qcsrc/server/mutators/gamemode_onslaught.qc @@ -0,0 +1,1703 @@ +float autocvar_g_onslaught_spawn_at_controlpoints; +float autocvar_g_onslaught_spawn_at_generator; +float autocvar_g_onslaught_controlpoints_proxycap; +float autocvar_g_onslaught_controlpoints_proxycap_distance = 512; +float autocvar_g_onslaught_controlpoints_proxycap_dps = 100; + +void onslaught_generator_updatesprite(entity e); +void onslaught_controlpoint_updatesprite(entity e); +void onslaught_link_checkupdate(); + +.entity sprite; +.string target2; +.float iscaptured; +.float islinked; +.float isgenneighbor_red; +.float isgenneighbor_blue; +.float iscpneighbor_red; +.float iscpneighbor_blue; +.float isshielded; +.float lasthealth; +.float lastteam; +.float lastshielded; +.float lastcaptured; + +.string model1, model2, model3; + +entity ons_red_generator; +entity ons_blue_generator; + +void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.velocity = self.velocity + vforce; +} + +.float giblifetime; +void ons_throwgib_think() +{ + float d; + + self.nextthink = time + 0.05; + + d = self.giblifetime - time; + + if(d<0) + { + self.think = SUB_Remove; + return; + } + if(d<1) + self.alpha = d; + + if(d>2) + if(random()<0.6) + pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1); +} + +void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn) +{ + entity gib; + + gib = spawn(); + + setmodel(gib, smodel); + setorigin(gib, v_from); + gib.solid = SOLID_BBOX; + gib.movetype = MOVETYPE_BOUNCE; + gib.takedamage = DAMAGE_YES; + gib.event_damage = ons_gib_damage; + gib.health = -1; + gib.effects = EF_LOWPRECISION; + gib.flags = FL_NOTARGET; + gib.velocity = v_to; + gib.giblifetime = time + f_lifetime; + + if (b_burn) + { + gib.think = ons_throwgib_think; + gib.nextthink = time + 0.05; + } + else + SUB_SetFade(gib, gib.giblifetime, 2); +} + +void onslaught_updatelinks() +{ + entity l, links; + float stop, t1, t2, t3, t4; + // first check if the game has ended + dprint("--- updatelinks ---\n"); + links = findchain(classname, "onslaught_link"); + // mark generators as being shielded and networked + l = findchain(classname, "onslaught_generator"); + while (l) + { + if (l.iscaptured) + dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"); + else + dprint(etos(l), " (generator) is destroyed\n"); + l.islinked = l.iscaptured; + l.isshielded = l.iscaptured; + l = l.chain; + } + // mark points as shielded and not networked + l = findchain(classname, "onslaught_controlpoint"); + while (l) + { + l.islinked = FALSE; + l.isshielded = TRUE; + l.isgenneighbor_red = FALSE; + l.isgenneighbor_blue = FALSE; + l.iscpneighbor_red = FALSE; + l.iscpneighbor_blue = FALSE; + dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n"); + l = l.chain; + } + // flow power outward from the generators through the network + l = links; + while (l) + { + dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n"); + l = l.chain; + } + stop = FALSE; + while (!stop) + { + stop = TRUE; + l = links; + while (l) + { + // if both points are captured by the same team, and only one of + // them is powered, mark the other one as powered as well + if (l.enemy.iscaptured && l.goalentity.iscaptured) + if (l.enemy.islinked != l.goalentity.islinked) + if (l.enemy.team == l.goalentity.team) + { + if (!l.goalentity.islinked) + { + stop = FALSE; + l.goalentity.islinked = TRUE; + dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"); + } + else if (!l.enemy.islinked) + { + stop = FALSE; + l.enemy.islinked = TRUE; + dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"); + } + } + l = l.chain; + } + } + // now that we know which points are powered we can mark their neighbors + // as unshielded if team differs + l = links; + while (l) + { + if (l.goalentity.islinked) + { + if (l.goalentity.team != l.enemy.team) + { + dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"); + l.enemy.isshielded = FALSE; + } + if(l.goalentity.classname == "onslaught_generator") + { + if(l.goalentity.team == COLOR_TEAM1) + l.enemy.isgenneighbor_red = TRUE; + else if(l.goalentity.team == COLOR_TEAM2) + l.enemy.isgenneighbor_blue = TRUE; + } + else + { + if(l.goalentity.team == COLOR_TEAM1) + l.enemy.iscpneighbor_red = TRUE; + else if(l.goalentity.team == COLOR_TEAM2) + l.enemy.iscpneighbor_blue = TRUE; + } + } + if (l.enemy.islinked) + { + if (l.goalentity.team != l.enemy.team) + { + dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"); + l.goalentity.isshielded = FALSE; + } + if(l.enemy.classname == "onslaught_generator") + { + if(l.enemy.team == COLOR_TEAM1) + l.goalentity.isgenneighbor_red = TRUE; + else if(l.enemy.team == COLOR_TEAM2) + l.goalentity.isgenneighbor_blue = TRUE; + } + else + { + if(l.enemy.team == COLOR_TEAM1) + l.goalentity.iscpneighbor_red = TRUE; + else if(l.enemy.team == COLOR_TEAM2) + l.goalentity.iscpneighbor_blue = TRUE; + } + } + l = l.chain; + } + // now update the takedamage and alpha variables on generator shields + l = findchain(classname, "onslaught_generator"); + while (l) + { + if (l.isshielded) + { + dprint(etos(l), " (generator) is shielded\n"); + l.enemy.alpha = 1; + l.takedamage = DAMAGE_NO; + l.bot_attack = FALSE; + } + else + { + dprint(etos(l), " (generator) is not shielded\n"); + l.enemy.alpha = -1; + l.takedamage = DAMAGE_AIM; + l.bot_attack = TRUE; + } + l = l.chain; + } + // now update the takedamage and alpha variables on control point icons + l = findchain(classname, "onslaught_controlpoint"); + while (l) + { + if (l.isshielded) + { + dprint(etos(l), " (point) is shielded\n"); + l.enemy.alpha = 1; + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_NO; + l.goalentity.bot_attack = FALSE; + } + } + else + { + dprint(etos(l), " (point) is not shielded\n"); + l.enemy.alpha = -1; + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_AIM; + l.goalentity.bot_attack = TRUE; + } + } + onslaught_controlpoint_updatesprite(l); + l = l.chain; + } + // count generators owned by each team + t1 = t2 = t3 = t4 = 0; + l = findchain(classname, "onslaught_generator"); + while (l) + { + if (l.iscaptured) + { + if (l.team == COLOR_TEAM1) t1 = 1; + if (l.team == COLOR_TEAM2) t2 = 1; + if (l.team == COLOR_TEAM3) t3 = 1; + if (l.team == COLOR_TEAM4) t4 = 1; + } + onslaught_generator_updatesprite(l); + l = l.chain; + } + // see if multiple teams remain (if not, it's game over) + if (t1 + t2 + t3 + t4 < 2) + dprint("--- game over ---\n"); + else + dprint("--- done updating links ---\n"); +} + +float onslaught_controlpoint_can_be_linked(entity cp, float t) +{ + if(t == COLOR_TEAM1) + { + if(cp.isgenneighbor_red) + return 2; + if(cp.iscpneighbor_red) + return 1; + } + else if(t == COLOR_TEAM2) + { + if(cp.isgenneighbor_blue) + return 2; + if(cp.iscpneighbor_blue) + return 1; + } + return 0; + /* + entity e; + // check to see if this player has a legitimate claim to capture this + // control point - more specifically that there is a captured path of + // points leading back to the team generator + e = findchain(classname, "onslaught_link"); + while (e) + { + if (e.goalentity == cp) + { + dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)"); + if (e.enemy.islinked) + { + dprint(" which is linked"); + if (e.enemy.team == t) + { + dprint(" and has the correct team!\n"); + return 1; + } + else + dprint(" but has the wrong team\n"); + } + else + dprint("\n"); + } + else if (e.enemy == cp) + { + dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)"); + if (e.goalentity.islinked) + { + dprint(" which is linked"); + if (e.goalentity.team == t) + { + dprint(" and has a team!\n"); + return 1; + } + else + dprint(" but has the wrong team\n"); + } + else + dprint("\n"); + } + e = e.chain; + } + return 0; + */ +} + +float onslaught_controlpoint_attackable(entity cp, float t) + // -2: SAME TEAM, attackable by enemy! + // -1: SAME TEAM! + // 0: off limits + // 1: attack it + // 2: touch it + // 3: attack it (HIGH PRIO) + // 4: touch it (HIGH PRIO) +{ + float a; + + if(cp.isshielded) + { + return 0; + } + else if(cp.goalentity) + { + // if there's already an icon built, nothing happens + if(cp.team == t) + { + a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t); + if(a) // attackable by enemy? + return -2; // EMERGENCY! + return -1; + } + // we know it can be linked, so no need to check + // but... + a = onslaught_controlpoint_can_be_linked(cp, t); + if(a == 2) // near our generator? + return 3; // EMERGENCY! + return 1; + } + else + { + // free point + if(onslaught_controlpoint_can_be_linked(cp, t)) + { + a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t); + if(a == 2) + return 4; // GET THIS ONE NOW! + else + return 2; // TOUCH ME + } + } + return 0; +} + +float overtime_msg_time; +void onslaught_generator_think() +{ + float d; + entity e; + self.nextthink = ceil(time + 1); + if (!gameover) + { + if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) + { + if (!overtime_msg_time) + { + FOR_EACH_PLAYER(e) + centerprint(e, "^3Now playing ^1OVERTIME^3!\n^3Generators start now to decay.\n^3The more control points your team holds,\n^3the faster the enemy generator decays."); + overtime_msg_time = time; + } + // self.max_health / 300 gives 5 minutes of overtime. + // control points reduce the overtime duration. + sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTN_NORM); + d = 1; + e = findchain(classname, "onslaught_controlpoint"); + while (e) + { + if (e.team != self.team) + if (e.islinked) + d = d + 1; + e = e.chain; + } + + if(autocvar_g_campaign && autocvar__campaign_testrun) + d = d * self.max_health; + else + d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath); + + Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0'); + } + else if (overtime_msg_time) + overtime_msg_time = 0; + + if(!self.isshielded && self.wait < time) + { + self.wait = time + 5; + FOR_EACH_PLAYER(e) + { + if(e.team == self.team) + { + centerprint(e, "^1Your generator is NOT shielded!\n^7Re-capture controlpoints to shield it!"); + soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTN_NONE); // FIXME: Uniqe sound? + } + } + } + } +} + +void onslaught_generator_ring_spawn(vector org) +{ + modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25); +} + +void onslaught_generator_ray_think() +{ + self.nextthink = time + 0.05; + if(self.count > 10) + { + self.think = SUB_Remove; + return; + } + + if(self.count > 5) + self.alpha -= 0.1; + else + self.alpha += 0.1; + + self.scale += 0.2; + self.count +=1; +} + +void onslaught_generator_ray_spawn(vector org) +{ + entity e; + e = spawn(); + setmodel(e, "models/onslaught/ons_ray.md3"); + setorigin(e, org); + e.angles = randomvec() * 360; + e.alpha = 0; + e.scale = random() * 5 + 8; + e.think = onslaught_generator_ray_think; + e.nextthink = time + 0.05; +} + +void onslaught_generator_shockwave_spawn(vector org) +{ + shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5); +} + +void onslaught_generator_damage_think() +{ + if(self.owner.health < 0) + { + self.think = SUB_Remove; + return; + } + self.nextthink = time+0.1; + + // damaged fx (less probable the more damaged is the generator) + if(random() < 0.9 - self.owner.health / self.owner.max_health) + if(random() < 0.01) + { + pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); + sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM); + } + else + pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); +} + +void onslaught_generator_damage_spawn(entity gd_owner) +{ + entity e; + e = spawn(); + e.owner = gd_owner; + e.health = self.owner.health; + setorigin(e, gd_owner.origin); + e.think = onslaught_generator_damage_think; + e.nextthink = time+1; +} + +void onslaught_generator_deaththink() +{ + vector org; + float i; + + if not (self.count) + self.count = 40; + + // White shockwave + if(self.count==40||self.count==20) + { + onslaught_generator_ring_spawn(self.origin); + sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM); + } + + // Throw some gibs + if(random() < 0.3) + { + i = random(); + if(i < 0.3) + ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE); + else if(i > 0.7) + ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE); + else + ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE); + } + + // Spawn fire balls + for(i=0;i < 10;++i) + { + org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); + pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1); + } + + // Short explosion sound + small explosion + if(random() < 0.25) + { + te_explosion(self.origin); + sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); + } + + // Particles + org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); + pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1); + + // rays + if(random() > 0.25 ) + { + onslaught_generator_ray_spawn(self.origin); + } + + // Final explosion + if(self.count==1) + { + org = self.origin; + te_explosion(org); + onslaught_generator_shockwave_spawn(org); + pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1); + sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + } + else + self.nextthink = time + 0.05; + + self.count = self.count - 1; +} + +void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + float i; + if (damage <= 0) + return; + if(inWarmupStage) + return; + if (attacker != self) + { + if (self.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (attacker.classname == "player") + { + play2(attacker, "onslaught/damageblockedbyshield.wav"); + self.pain_finished = time + 1; + } + return; + } + if (time > self.pain_finished) + { + self.pain_finished = time + 10; + bprint(ColoredTeamName(self.team), " generator under attack!\n"); + play2team(self.team, "onslaught/generator_underattack.wav"); + } + } + self.health = self.health - damage; + WaypointSprite_UpdateHealth(self.sprite, self.health); + // choose an animation frame based on health + self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); + // see if the generator is still functional, or dying + if (self.health > 0) + { +#ifdef ONSLAUGHT_SPAM + float h, lh; + lh = ceil(self.lasthealth / 100) * 100; + h = ceil(self.health / 100) * 100; + if(lh != h) + bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n"); +#endif + self.lasthealth = self.health; + } + else if not(inWarmupStage) + { + if (attacker == self) + bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n"); + else + { + string t; + t = ColoredTeamName(attacker.team); + bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n"); + } + self.iscaptured = FALSE; + self.islinked = FALSE; + self.isshielded = FALSE; + self.takedamage = DAMAGE_NO; // can't be hurt anymore + self.event_damage = SUB_Null; // won't do anything if hurt + self.count = 0; // reset counter + self.think = onslaught_generator_deaththink; // explosion sequence + self.nextthink = time; // start exploding immediately + self.think(); // do the first explosion now + + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + + onslaught_updatelinks(); + } + + if(self.health <= 0) + setmodel(self, "models/onslaught/generator_dead.md3"); + else if(self.health < self.max_health * 0.10) + setmodel(self, "models/onslaught/generator_dmg9.md3"); + else if(self.health < self.max_health * 0.20) + setmodel(self, "models/onslaught/generator_dmg8.md3"); + else if(self.health < self.max_health * 0.30) + setmodel(self, "models/onslaught/generator_dmg7.md3"); + else if(self.health < self.max_health * 0.40) + setmodel(self, "models/onslaught/generator_dmg6.md3"); + else if(self.health < self.max_health * 0.50) + setmodel(self, "models/onslaught/generator_dmg5.md3"); + else if(self.health < self.max_health * 0.60) + setmodel(self, "models/onslaught/generator_dmg4.md3"); + else if(self.health < self.max_health * 0.70) + setmodel(self, "models/onslaught/generator_dmg3.md3"); + else if(self.health < self.max_health * 0.80) + setmodel(self, "models/onslaught/generator_dmg2.md3"); + else if(self.health < self.max_health * 0.90) + setmodel(self, "models/onslaught/generator_dmg1.md3"); + setsize(self, '-52 -52 -14', '52 52 75'); + + // Throw some flaming gibs on damage, more damage = more chance for gib + if(random() < damage/220) + { + sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + i = random(); + if(i < 0.3) + ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE); + else if(i > 0.7) + ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE); + else + ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE); + } + else + { + // particles on every hit + pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1); + + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM); + else + sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM); + } + + //throw some gibs on damage + if(random() < damage/200+0.2) + if(random() < 0.5) + ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE); +} + +// update links after a delay +void onslaught_generator_delayed() +{ + onslaught_updatelinks(); + // now begin normal thinking + self.think = onslaught_generator_think; + self.nextthink = time; +} + +string onslaught_generator_waypointsprite_for_team(entity e, float t) +{ + if(t == e.team) + { + if(e.team == COLOR_TEAM1) + return "ons-gen-red"; + else if(e.team == COLOR_TEAM2) + return "ons-gen-blue"; + } + if(e.isshielded) + return "ons-gen-shielded"; + if(e.team == COLOR_TEAM1) + return "ons-gen-red"; + else if(e.team == COLOR_TEAM2) + return "ons-gen-blue"; + return ""; +} + +void onslaught_generator_updatesprite(entity e) +{ + string s1, s2, s3; + s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1); + s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2); + s3 = onslaught_generator_waypointsprite_for_team(e, -1); + WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3); + + if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) + { + e.lastteam = e.team + 2; + e.lastshielded = e.isshielded; + if(e.lastshielded) + { + if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); + } + else + { + if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + } +} + +string onslaught_controlpoint_waypointsprite_for_team(entity e, float t) +{ + float a; + if(t != -1) + { + a = onslaught_controlpoint_attackable(e, t); + if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW + { + if(e.team == COLOR_TEAM1) + return "ons-cp-atck-red"; + else if(e.team == COLOR_TEAM2) + return "ons-cp-atck-blue"; + else + return "ons-cp-atck-neut"; + } + else if(a == -2) // DEFEND THIS ONE NOW + { + if(e.team == COLOR_TEAM1) + return "ons-cp-dfnd-red"; + else if(e.team == COLOR_TEAM2) + return "ons-cp-dfnd-blue"; + } + else if(e.team == t || a == -1 || a == 1) // own point, or fire at it + { + if(e.team == COLOR_TEAM1) + return "ons-cp-red"; + else if(e.team == COLOR_TEAM2) + return "ons-cp-blue"; + } + else if(a == 2) // touch it + return "ons-cp-neut"; + } + else + { + if(e.team == COLOR_TEAM1) + return "ons-cp-red"; + else if(e.team == COLOR_TEAM2) + return "ons-cp-blue"; + else + return "ons-cp-neut"; + } + return ""; +} + +void onslaught_controlpoint_updatesprite(entity e) +{ + string s1, s2, s3; + s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1); + s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2); + s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1); + WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3); + + float sh; + sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2)); + + if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) + { + if(e.iscaptured) // don't mess up build bars! + { + if(sh) + { + WaypointSprite_UpdateMaxHealth(e.sprite, 0); + } + else + { + WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); + WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); + } + } + if(e.lastshielded) + { + if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); + } + else + { + if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + + e.lastteam = e.team + 2; + e.lastshielded = sh; + e.lastcaptured = e.iscaptured; + } +} + +void onslaught_generator_reset() +{ + self.team = self.team_saved; + self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; + self.takedamage = DAMAGE_AIM; + self.bot_attack = TRUE; + self.iscaptured = TRUE; + self.islinked = TRUE; + self.isshielded = TRUE; + self.enemy.solid = SOLID_NOT; + self.think = onslaught_generator_delayed; + self.nextthink = time + 0.2; + setmodel(self, "models/onslaught/generator.md3"); + setsize(self, '-52 -52 -14', '52 52 75'); + + if (!self.noalign) + droptofloor(); + + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); +} + +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) + Base generator. + + spawnfunc_onslaught_link entities can target this. + +keys: +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. + */ +void spawnfunc_onslaught_generator() +{ + if (!g_onslaught) + { + remove(self); + return; + } + + //entity e; + precache_model("models/onslaught/generator.md3"); + precache_model("models/onslaught/generator_shield.md3"); + precache_model("models/onslaught/generator_dmg1.md3"); + precache_model("models/onslaught/generator_dmg2.md3"); + precache_model("models/onslaught/generator_dmg3.md3"); + precache_model("models/onslaught/generator_dmg4.md3"); + precache_model("models/onslaught/generator_dmg5.md3"); + precache_model("models/onslaught/generator_dmg6.md3"); + precache_model("models/onslaught/generator_dmg7.md3"); + precache_model("models/onslaught/generator_dmg8.md3"); + precache_model("models/onslaught/generator_dmg9.md3"); + precache_model("models/onslaught/generator_dead.md3"); + precache_model("models/onslaught/shockwave.md3"); + precache_model("models/onslaught/shockwavetransring.md3"); + precache_model("models/onslaught/gen_gib1.md3"); + precache_model("models/onslaught/gen_gib2.md3"); + precache_model("models/onslaught/gen_gib3.md3"); + precache_model("models/onslaught/ons_ray.md3"); + precache_sound("onslaught/generator_decay.wav"); + precache_sound("weapons/grenade_impact.wav"); + precache_sound("weapons/rocket_impact.wav"); + precache_sound("onslaught/generator_underattack.wav"); + precache_sound("onslaught/shockwave.wav"); + precache_sound("onslaught/ons_hit1.wav"); + precache_sound("onslaught/ons_hit2.wav"); + precache_sound("onslaught/electricity_explode.wav"); + if (!self.team) + objerror("team must be set"); + + if(self.team == COLOR_TEAM1) + ons_red_generator = self; + + if(self.team == COLOR_TEAM2) + ons_blue_generator = self; + + self.team_saved = self.team; + self.colormap = 1024 + (self.team - 1) * 17; + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; + setmodel(self, "models/onslaught/generator.md3"); + setsize(self, '-52 -52 -14', '52 52 75'); + setorigin(self, self.origin); + self.takedamage = DAMAGE_AIM; + self.bot_attack = TRUE; + self.event_damage = onslaught_generator_damage; + self.iscaptured = TRUE; + self.islinked = TRUE; + self.isshielded = TRUE; + // helper entity that create fx when generator is damaged + onslaught_generator_damage_spawn(self); + // spawn shield model which indicates whether this can be damaged + self.enemy = spawn(); + setattachment(self.enemy , self, ""); + self.enemy.classname = "onslaught_generator_shield"; + self.enemy.solid = SOLID_NOT; + self.enemy.movetype = MOVETYPE_NONE; + self.enemy.effects = EF_ADDITIVE; + setmodel(self.enemy, "models/onslaught/generator_shield.md3"); + //setorigin(e, self.origin); + self.enemy.colormap = self.colormap; + self.enemy.team = self.team; + //self.think = onslaught_generator_delayed; + //self.nextthink = time + 0.2; + InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST); + + WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0'); + WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY); + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + + waypoint_spawnforitem(self); + + onslaught_updatelinks(); + + self.reset = onslaught_generator_reset; +} + +.float waslinked; +.float cp_bob_spd; +.vector cp_origin, cp_bob_origin, cp_bob_dmg; + +float ons_notification_time_team1; +float ons_notification_time_team2; + +void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + entity oself; + float nag; + + if (damage <= 0) + return; + if (self.owner.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (attacker.classname == "player") + { + play2(attacker, "onslaught/damageblockedbyshield.wav"); + self.pain_finished = time + 1; + } + return; + } + + if (attacker.classname == "player") + { + nag = FALSE; + if(self.team == COLOR_TEAM1) + { + if(time - ons_notification_time_team1 > 10) + { + nag = TRUE; + ons_notification_time_team1 = time; + } + } + else if(self.team == COLOR_TEAM2) + { + if(time - ons_notification_time_team2 > 10) + { + nag = TRUE; + ons_notification_time_team2 = time; + } + } + else + nag = TRUE; + + if(nag) + play2team(self.team, "onslaught/controlpoint_underattack.wav"); + } + + self.health = self.health - damage; + if(self.owner.iscaptured) + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + else + WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime)); + self.pain_finished = time + 1; + self.punchangle = (2 * randomvec() - '1 1 1') * 45; + self.cp_bob_dmg_z = (2 * random() - 1) * 15; + // colormod flash when shot + self.colormod = '2 2 2'; + // particles on every hit + pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1); + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM); + else + sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM); + + if (self.health < 0) + { + sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + { + string t; + t = ColoredTeamName(attacker.team); + bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n"); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); + ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE); + } + self.owner.goalentity = world; + self.owner.islinked = FALSE; + self.owner.iscaptured = FALSE; + self.owner.team = 0; + self.owner.colormap = 1024; + + WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; + + + self.owner.waslinked = self.owner.islinked; + if(self.owner.model != "models/onslaught/controlpoint_pad.md3") + setmodel(self.owner, "models/onslaught/controlpoint_pad.md3"); + //setsize(self, '-32 -32 0', '32 32 8'); + + remove(self); + } +} + +void onslaught_controlpoint_icon_think() +{ + entity oself; + self.nextthink = time + sys_frametime; + + if(autocvar_g_onslaught_controlpoints_proxycap) + { + float _enemy_count; + float _friendly_count; + float _dist; + entity _player; + + FOR_EACH_PLAYER(_player) + { + if(!_player.deadflag) + { + _dist = vlen(_player.origin - self.origin); + if(_dist < autocvar_g_onslaught_controlpoints_proxycap_distance) + { + if(_player.team == self.team) + ++_friendly_count; + else + ++_enemy_count; + } + } + } + + _friendly_count = _friendly_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime); + _enemy_count = _enemy_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime); + + self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); + if(self.health <= 0) + { + onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0'); + return; + } + } + + if (time > self.pain_finished + 5) + { + if(self.health < self.max_health) + { + self.health = self.health + self.count; + if (self.health >= self.max_health) + self.health = self.max_health; + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + } + } + if (self.health < self.max_health * 0.25) + setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3"); + else if (self.health < self.max_health * 0.50) + setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3"); + else if (self.health < self.max_health * 0.75) + setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3"); + else if (self.health < self.max_health * 0.90) + setmodel(self, "models/onslaught/controlpoint_icon.md3"); + // colormod flash when shot + self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); + + if(self.owner.islinked != self.owner.waslinked) + { + // unteam the spawnpoint if needed + float t; + t = self.owner.team; + if(!self.owner.islinked) + self.owner.team = 0; + + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; + + self.owner.team = t; + + self.owner.waslinked = self.owner.islinked; + } + + if (self.punchangle_x > 0) + { + self.punchangle_x = self.punchangle_x - 60 * sys_frametime; + if (self.punchangle_x < 0) + self.punchangle_x = 0; + } + else if (self.punchangle_x < 0) + { + self.punchangle_x = self.punchangle_x + 60 * sys_frametime; + if (self.punchangle_x > 0) + self.punchangle_x = 0; + } + + if (self.punchangle_y > 0) + { + self.punchangle_y = self.punchangle_y - 60 * sys_frametime; + if (self.punchangle_y < 0) + self.punchangle_y = 0; + } + else if (self.punchangle_y < 0) + { + self.punchangle_y = self.punchangle_y + 60 * sys_frametime; + if (self.punchangle_y > 0) + self.punchangle_y = 0; + } + + if (self.punchangle_z > 0) + { + self.punchangle_z = self.punchangle_z - 60 * sys_frametime; + if (self.punchangle_z < 0) + self.punchangle_z = 0; + } + else if (self.punchangle_z < 0) + { + self.punchangle_z = self.punchangle_z + 60 * sys_frametime; + if (self.punchangle_z > 0) + self.punchangle_z = 0; + } + + self.angles_x = self.punchangle_x; + self.angles_y = self.punchangle_y + self.mangle_y; + self.angles_z = self.punchangle_z; + self.mangle_y = self.mangle_y + 45 * sys_frametime; + + self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd)); + self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime; + if(self.cp_bob_dmg_z > 0) + self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * sys_frametime; + else + self.cp_bob_dmg_z = 0; + setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg); + + // damaged fx + if(random() < 0.6 - self.health / self.max_health) + { + pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); + + if(random() > 0.8) + sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM); + else if (random() > 0.5) + sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM); + } +} + +void onslaught_controlpoint_icon_buildthink() +{ + entity oself; + float a; + + self.nextthink = time + sys_frametime; + + // only do this if there is power + a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team); + if(!a) + return; + + self.health = self.health + self.count; + + if (self.health >= self.max_health) + { + self.health = self.max_health; + self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on + self.think = onslaught_controlpoint_icon_think; + sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM); + bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n"); + self.owner.iscaptured = TRUE; + + WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + oself = self; + self = self.owner; + activator = self; + SUB_UseTargets (); + self = oself; + self.cp_origin = self.origin; + self.cp_bob_origin = '0 0 0.1'; + self.cp_bob_spd = 0; + } + self.alpha = self.health / self.max_health; + // colormod flash when shot + self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1)); + if(self.owner.model != "models/onslaught/controlpoint_pad2.md3") + setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3"); + //setsize(self, '-32 -32 0', '32 32 8'); + + if(random() < 0.9 - self.health / self.max_health) + pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1); +} + + + + +void onslaught_controlpoint_touch() +{ + entity e; + float a; + if (other.classname != "player") + return; + a = onslaught_controlpoint_attackable(self, other.team); + if(a != 2 && a != 4) + return; + // we've verified that this player has a legitimate claim to this point, + // so start building the captured point icon (which only captures this + // point if it successfully builds without being destroyed first) + self.goalentity = e = spawn(); + e.classname = "onslaught_controlpoint_icon"; + e.owner = self; + e.max_health = autocvar_g_onslaught_cp_health; + e.health = autocvar_g_onslaught_cp_buildhealth; + e.solid = SOLID_BBOX; + e.movetype = MOVETYPE_NONE; + setmodel(e, "models/onslaught/controlpoint_icon.md3"); + setsize(e, '-32 -32 -32', '32 32 32'); + setorigin(e, self.origin + '0 0 96'); + e.takedamage = DAMAGE_AIM; + e.bot_attack = TRUE; + e.event_damage = onslaught_controlpoint_icon_damage; + e.team = other.team; + e.colormap = 1024 + (e.team - 1) * 17; + e.think = onslaught_controlpoint_icon_buildthink; + e.nextthink = time + sys_frametime; + e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM); + self.team = e.team; + self.colormap = e.colormap; + WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime)); + onslaught_updatelinks(); +} + +void onslaught_controlpoint_reset() +{ + if(self.goalentity && self.goalentity != world) + remove(self.goalentity); + self.goalentity = world; + self.team = 0; + self.colormap = 1024; + self.iscaptured = FALSE; + self.islinked = FALSE; + self.isshielded = TRUE; + self.enemy.solid = SOLID_NOT; + self.enemy.colormap = self.colormap; + self.think = self.enemy.think = SUB_Null; + self.nextthink = 0; // don't like SUB_Null :P + setmodel(self, "models/onslaught/controlpoint_pad.md3"); + //setsize(self, '-32 -32 0', '32 32 8'); + + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + + onslaught_updatelinks(); + + activator = self; + SUB_UseTargets(); // to reset the structures, playerspawns etc. +} + +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) + Control point. Be sure to give this enough clearance so that the shootable part has room to exist + + This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. + +keys: +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) + */ + + /* +void onslaught_controlpoint_think() +{ + self.nextthink = time; + //if(autocvar_g_onslaught_controlpoints_proxycap) + + float _enemy_count; + float _friendly_count; + float _dist; + entity _player; + + FOR_EACH_PLAYER(_player) + { + if(!_player.deadflag) + { + _dist = vlen(_player.origin - self.origin); + if(_dist < autocvar_g_onslaught_controlpoints_proxycap_distance) + { + if(_player.team == self.team) + ++_friendly_count; + else + ++_enemy_count; + } + } + } + + _friendly_count = _friendly_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime); + _enemy_count = _enemy_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime); + + self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); + if(self.health <= 0) + { + onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0'); + return; + } + + if(self.health == max_health) + { + + } +} +*/ + +void spawnfunc_onslaught_controlpoint() +{ + //entity e; + if (!g_onslaught) + { + remove(self); + return; + } + precache_model("models/onslaught/controlpoint_pad.md3"); + precache_model("models/onslaught/controlpoint_pad2.md3"); + precache_model("models/onslaught/controlpoint_shield.md3"); + precache_model("models/onslaught/controlpoint_icon.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg1.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg2.md3"); + precache_model("models/onslaught/controlpoint_icon_dmg3.md3"); + precache_model("models/onslaught/controlpoint_icon_gib1.md3"); + precache_model("models/onslaught/controlpoint_icon_gib2.md3"); + precache_model("models/onslaught/controlpoint_icon_gib4.md3"); + precache_sound("onslaught/controlpoint_build.wav"); + precache_sound("onslaught/controlpoint_built.wav"); + precache_sound("weapons/grenade_impact.wav"); + precache_sound("onslaught/damageblockedbyshield.wav"); + precache_sound("onslaught/controlpoint_underattack.wav"); + precache_sound("onslaught/ons_spark1.wav"); + precache_sound("onslaught/ons_spark2.wav"); + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + setmodel(self, "models/onslaught/controlpoint_pad.md3"); + //setsize(self, '-32 -32 0', '32 32 8'); + if (!self.noalign) + droptofloor(); + + setorigin(self, self.origin); + self.touch = onslaught_controlpoint_touch; + self.team = 0; + self.colormap = 1024; + self.iscaptured = FALSE; + self.islinked = FALSE; + self.isshielded = TRUE; + // spawn shield model which indicates whether this can be damaged + self.enemy = spawn(); + self.enemy.classname = "onslaught_controlpoint_shield"; + self.enemy.solid = SOLID_NOT; + self.enemy.movetype = MOVETYPE_NONE; + self.enemy.effects = EF_ADDITIVE; + setmodel(self.enemy , "models/onslaught/controlpoint_shield.md3"); + + setattachment(self.enemy , self, ""); + //setsize(e, '-32 -32 0', '32 32 128'); + + //setorigin(e, self.origin); + self.enemy.colormap = self.colormap; + + waypoint_spawnforitem(self); + + WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0'); + WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); + + self.reset = onslaught_controlpoint_reset; +} + +float onslaught_link_send(entity to, float sendflags) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & 1) + { + WriteCoord(MSG_ENTITY, self.goalentity.origin_x); + WriteCoord(MSG_ENTITY, self.goalentity.origin_y); + WriteCoord(MSG_ENTITY, self.goalentity.origin_z); + } + if(sendflags & 2) + { + WriteCoord(MSG_ENTITY, self.enemy.origin_x); + WriteCoord(MSG_ENTITY, self.enemy.origin_y); + WriteCoord(MSG_ENTITY, self.enemy.origin_z); + } + if(sendflags & 4) + { + WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16 + } + return TRUE; +} + +void onslaught_link_checkupdate() +{ + // TODO check if the two sides have moved (currently they won't move anyway) + float redpower, bluepower; + + redpower = bluepower = 0; + if(self.goalentity.islinked) + { + if(self.goalentity.team == COLOR_TEAM1) + redpower = 1; + else if(self.goalentity.team == COLOR_TEAM2) + bluepower = 1; + } + if(self.enemy.islinked) + { + if(self.enemy.team == COLOR_TEAM1) + redpower = 2; + else if(self.enemy.team == COLOR_TEAM2) + bluepower = 2; + } + + float cc; + if(redpower == 1 && bluepower == 2) + cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10; + else if(redpower == 2 && bluepower == 1) + cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01; + else if(redpower) + cc = (COLOR_TEAM1 - 1) * 0x11; + else if(bluepower) + cc = (COLOR_TEAM2 - 1) * 0x11; + else + cc = 0; + + //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " "); + //print("cc=", ftos(cc), "\n"); + + if(cc != self.clientcolors) + { + self.clientcolors = cc; + self.SendFlags |= 4; + } + + self.nextthink = time; +} + +void onslaught_link_delayed() +{ + self.goalentity = find(world, targetname, self.target); + self.enemy = find(world, targetname, self.target2); + if (!self.goalentity) + objerror("can not find target\n"); + if (!self.enemy) + objerror("can not find target2\n"); + dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n"); + self.SendFlags |= 3; + self.think = onslaught_link_checkupdate; + self.nextthink = time; +} + +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) + Link between control points. + + This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. + +keys: +"target" - first control point. +"target2" - second control point. + */ +void spawnfunc_onslaught_link() +{ + if (!g_onslaught) + { + remove(self); + return; + } + if (self.target == "" || self.target2 == "") + objerror("target and target2 must be set\n"); + InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET); + Net_LinkEntity(self, FALSE, 0, onslaught_link_send); +} + +MUTATOR_HOOKFUNCTION(ons_BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":ONS"); + return 0; +} + +MUTATOR_HOOKFUNCTION(ons_BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Onslught"); + return 0; +} + +MUTATOR_HOOKFUNCTION(ons_Spawn_Score) +{ + + /* + float _neer_home = (random() > 0.5 ? TRUE : FALSE); + + RandomSelection_Init(); + + if(self.team == COLOR_TEAM1) + RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1); + + if(self.team == COLOR_TEAM2) + RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1); + + entity _cp = findchain(classname, "onslaught_controlpoint"): + while _cp; + { + if(_cp.team == self.team) + RandomSelection_Add(_cp, 0, string_null, 1, 1); + + _cp = _cp.chain; + } + + if(RandomSelection_chosen_ent) + { + self.tur_head = RandomSelection_chosen_ent; + spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + } + else if(self.team == spawn_spot.team) + spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate + + */ + + return 0; +} + +MUTATOR_HOOKFUNCTION(ons_PlayerSpawn) +{ + if(!autocvar_g_onslaught_spawn_at_controlpoints) + return 0; + + if(random() < 0.5) // 50/50 chane to use default spawnsystem. + return 0; + + float _close_to_home = ((random() > 0.5) ? TRUE : FALSE); + entity _best, _trg_gen; + float _score, _best_score = MAX_SHOT_DISTANCE; + + RandomSelection_Init(); + + if(self.team == COLOR_TEAM1) + { + if(!_close_to_home) + _trg_gen = ons_blue_generator; + else + _trg_gen = ons_red_generator; + } + + if(self.team == COLOR_TEAM2) + { + if(_close_to_home) + _trg_gen = ons_blue_generator; + else + _trg_gen = ons_red_generator; + } + + entity _cp = findchain(classname, "onslaught_controlpoint"); + while(_cp) + { + if(_cp.team == self.team) + { + _score = vlen(_trg_gen.origin - _cp.origin); + if(_score < _best_score) + { + _best = _cp; + _best_score = _score; + } + } + _cp = _cp.chain; + } + + vector _loc; + float i; + if(_best) + { + for(i = 0; i < 10; ++i) + { + _loc = _best.origin + '0 0 96'; + _loc += ('0 1 0' * random()) * 128; + tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, _loc); + self.angles = normalize(_loc - _best.origin) * RAD2DEG; + return 0; + } + } + } + else + { + if(!autocvar_g_onslaught_spawn_at_generator) + return 0; + + _trg_gen = ((self.team == COLOR_TEAM1) ? ons_red_generator : ons_blue_generator); + + for(i = 0; i < 10; ++i) + { + _loc = _trg_gen.origin + '0 0 96'; + _loc += ('0 1 0' * random()) * 128; + tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, _loc); + self.angles = normalize(_loc - _trg_gen.origin) * RAD2DEG; + return 0; + } + } + } + + return 0; +} + +MUTATOR_DEFINITION(gamemode_onslaught) +{ + //MUTATOR_HOOK(PlayerDies, nexball_BallDrop, CBC_ORDER_ANY); + //MUTATOR_HOOK(MakePlayerObserver, nexball_BallDrop, CBC_ORDER_ANY); + //MUTATOR_HOOK(ClientDisconnect, nexball_BallDrop, CBC_ORDER_ANY); + //MUTATOR_HOOK(PlayerPreThink, nexball_PlayerPreThink, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY); + //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + //InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE); + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator_dodging.qc b/qcsrc/server/mutators/mutator_dodging.qc index 3b193c163c..bdd39b1283 100644 --- a/qcsrc/server/mutators/mutator_dodging.qc +++ b/qcsrc/server/mutators/mutator_dodging.qc @@ -55,6 +55,9 @@ MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) { float velocity_difference; float clean_up_and_do_nothing; + if (self.deadflag != DEAD_NO) + return 0; + new_velocity_gain = 0; clean_up_and_do_nothing = 0; @@ -241,8 +244,6 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) { } } - - if (dodge_detected == 1) { self.last_dodging_time = time; @@ -255,7 +256,7 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) { self.dodging_direction_y = tap_direction_y; // normalize the dodging_direction vector.. (unlike UT99) XD - length = length + self.dodging_direction_x * self.dodging_direction_x; + length = self.dodging_direction_x * self.dodging_direction_x; length = length + self.dodging_direction_y * self.dodging_direction_y; length = sqrt(length); diff --git a/qcsrc/server/mutators/mutator_new_toys.qc b/qcsrc/server/mutators/mutator_new_toys.qc new file mode 100644 index 0000000000..cc4f94324b --- /dev/null +++ b/qcsrc/server/mutators/mutator_new_toys.qc @@ -0,0 +1,217 @@ +/* + +CORE laser nex lg rl cry gl elec hagar fireb hook + minsta porto + tuba + +NEW rifle hlac minel seeker +IDEAS OPEN flak OPEN FUN FUN FUN FUN + + + +How this mutator works: + ======================= + +When a gun tries to spawn, this mutator is called. It will provide alternate +weaponreplace lists. + +Entity: + +{ +"classname" "weapon_nex" +"new_toys" "rifle" +} +-> This will spawn as Rifle in this mutator ONLY, and as Nex otherwise. + +{ +"classname" "weapon_nex" +"new_toys" "nex rifle" +} +-> This will spawn as either Nex or Rifle in this mutator ONLY, and as Nex otherwise. + +{ +"classname" "weapon_nex" +"new_toys" "nex" +} +-> This is always a Nex. + +If the map specifies no "new_toys" argument + +There will be two default replacements selectable: "replace all" and "replace random". +In "replace all" mode, e.g. Nex will have the default replacement "rifle". +In "replace random" mode, Nex will have the default replacement "nex rifle". + +This mutator's replacements run BEFORE regular weaponreplace! + +The New Toys guns do NOT get a spawn function, so they can only ever be spawned +when this mutator is active. + +Likewise, warmup, give all, give ALL and impulse 99 will not give them unless +this mutator is active. + +Outside this mutator, they still can be spawned by: +- setting their start weapon cvar to 1 +- give weaponname +- weaponreplace +- weaponarena (but all and most weapons arena again won't include them) + +This mutator performs the default replacements on the DEFAULTS of the +start weapon selection. + +These weapons appear in the menu's priority list, BUT get a suffix +"(Mutator weapon)". + +Picking up a "new toys" weapon will not play standard weapon pickup sound, but +roflsound "New toys, new toys!" sound. + +*/ + +.string new_toys; + +float autocvar_g_new_toys_autoreplace; +#define NT_AUTOREPLACE_NEVER 0 +#define NT_AUTOREPLACE_ALWAYS 1 +#define NT_AUTOREPLACE_RANDOM 2 + +MUTATOR_HOOKFUNCTION(nt_SetModname) +{ + modname = "NewToys"; + return 0; +} + +float nt_IsNewToy(float w) +{ + switch(w) + { + case WEP_SEEKER: + case WEP_MINE_LAYER: + case WEP_HLAC: + case WEP_RIFLE: + return TRUE; + default: + return FALSE; + } +} + +string nt_GetFullReplacement(string w) +{ + switch(w) + { + case "hagar": return "seeker"; + case "rocketlauncher": return "minelayer"; + case "uzi": return "hlac"; + case "nex": return "rifle"; + default: return string_null; + } +} + +string nt_GetReplacement(string w, float m) +{ + if(m == NT_AUTOREPLACE_NEVER) + return w; + string s = nt_GetFullReplacement(w); + if not(s) + return w; + if(m == NT_AUTOREPLACE_RANDOM) + s = strcat(w, " ", s); + return s; +} + +MUTATOR_HOOKFUNCTION(nt_SetStartItems) +{ + // rearrange start_weapon_default + // apply those bits that are set by start_weapon_defaultmask + // same for warmup + + float i, j, k, n; + + WEPSET_DECLARE_A(newdefault); + WEPSET_DECLARE_A(warmup_newdefault); + + WEPSET_CLEAR_A(newdefault); + WEPSET_CLEAR_A(warmup_newdefault); + + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + entity e = get_weaponinfo(i); + if(!e.weapon) + continue; + + n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace)); + + for(j = 0; j < n; ++j) + for(k = WEP_FIRST; k <= WEP_LAST; ++k) + if(get_weaponinfo(k).netname == argv(j)) + { + if(WEPSET_CONTAINS_AW(start_weapons, i)) + WEPSET_OR_AW(newdefault, k); + if(WEPSET_CONTAINS_AW(warmup_start_weapons, i)) + WEPSET_OR_AW(warmup_newdefault, k); + } + } + + WEPSET_AND_AA(newdefault, start_weapons_defaultmask); + WEPSET_ANDNOT_AA(start_weapons, start_weapons_defaultmask); + WEPSET_OR_AA(start_weapons, newdefault); + + WEPSET_AND_AA(warmup_newdefault, warmup_start_weapons_defaultmask); + WEPSET_ANDNOT_AA(warmup_start_weapons, warmup_start_weapons_defaultmask); + WEPSET_OR_AA(warmup_start_weapons, warmup_newdefault); + + return 0; +} + +MUTATOR_HOOKFUNCTION(nt_SetWeaponreplace) +{ + // otherwise, we do replace + if(self.new_toys) + { + // map defined replacement: + ret_string = self.new_toys; + } + else + { + // auto replacement: + ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace); + } + + // apply regular weaponreplace + ret_string = W_Apply_Weaponreplace(ret_string); + + return 0; +} + +MUTATOR_HOOKFUNCTION(nt_FilterItem) +{ + if(nt_IsNewToy(self.weapon)) + self.item_pickupsound = "weapons/weaponpickup_new_toys.ogg"; + return 0; +} + +MUTATOR_DEFINITION(mutator_new_toys) +{ + MUTATOR_HOOK(SetModname, nt_SetModname, CBC_ORDER_ANY); + MUTATOR_HOOK(SetStartItems, nt_SetStartItems, CBC_ORDER_ANY); + MUTATOR_HOOK(SetWeaponreplace, nt_SetWeaponreplace, CBC_ORDER_LAST); + MUTATOR_HOOK(FilterItem, nt_FilterItem, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This cannot be added at runtime\n"); + + precache_sound("weapons/weaponpickup_new_toys.ogg"); + + // mark the guns as ok to use by e.g. impulse 99 + float i; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(nt_IsNewToy(i)) + get_weaponinfo(i).spawnflags &~= WEP_FLAG_MUTATORBLOCKED; + } + MUTATOR_ONREMOVE + { + error("This cannot be removed at runtime\n"); + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator_nix.qc b/qcsrc/server/mutators/mutator_nix.qc index 28bb844690..dad19e4a37 100644 --- a/qcsrc/server/mutators/mutator_nix.qc +++ b/qcsrc/server/mutators/mutator_nix.qc @@ -9,28 +9,23 @@ float nix_nextweapon_ammo; .float nix_lastinfotime; .float nix_nextincr; -.float nix_save_cells; -.float nix_save_shells; -.float nix_save_nails; -.float nix_save_rockets; -.float nix_save_fuel; -.float nix_save_weapons; - float NIX_CanChooseWeapon(float wpn) { entity e; e = get_weaponinfo(wpn); - if(!e.weapons) // skip dummies + if(!e.weapon) // skip dummies return FALSE; if(g_weaponarena) { - if not(g_weaponarena & e.weapons) + if not(WEPSET_CONTAINS_AW(g_weaponarena_weapons, wpn)) return FALSE; } else { if(wpn == WEP_LASER && g_nix_with_laser) return FALSE; + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + return FALSE; if not(e.spawnflags & WEP_FLAG_NORMAL) return FALSE; } @@ -141,10 +136,10 @@ void NIX_GiveCurrentWeapon() self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; } - self.weapons = 0; + WEPSET_CLEAR_E(self); if(g_nix_with_laser) - self.weapons = self.weapons | WEPBIT_LASER; - self.weapons = self.weapons | W_WeaponBit(nix_weapon); + WEPSET_ANDNOT_EW(self, WEP_LASER); + WEPSET_OR_EW(self, nix_weapon); if(self.switchweapon != nix_weapon) if(!client_hasweapon(self, self.switchweapon, TRUE, FALSE)) @@ -165,15 +160,6 @@ MUTATOR_HOOKFUNCTION(nix_ForbidThrowCurrentWeapon) return 1; // no throwing in NIX } -MUTATOR_HOOKFUNCTION(nix_SetStartItems) -{ - NIX_precache(); - // we do NOT change the start weapons any more, so we can later turn off the mutator! - // start_weapons = 0; // will be done later, when player spawns - // warmup_start_weapons = 0; // will be done later, when player spawns - return 0; -} - MUTATOR_HOOKFUNCTION(nix_BuildMutatorsString) { ret_string = strcat(ret_string, ":NIX"); @@ -232,18 +218,24 @@ MUTATOR_HOOKFUNCTION(nix_PlayerSpawn) return 0; } +MUTATOR_HOOKFUNCTION(nix_SetModname) +{ + modname = "NIX"; + return 0; +} + MUTATOR_DEFINITION(mutator_nix) { entity e; MUTATOR_HOOK(ForbidThrowCurrentWeapon, nix_ForbidThrowCurrentWeapon, CBC_ORDER_ANY); - MUTATOR_HOOK(SetStartItems, nix_SetStartItems, CBC_ORDER_EXCLUSIVE); MUTATOR_HOOK(BuildMutatorsString, nix_BuildMutatorsString, CBC_ORDER_ANY); MUTATOR_HOOK(BuildMutatorsPrettyString, nix_BuildMutatorsPrettyString, CBC_ORDER_ANY); MUTATOR_HOOK(FilterItem, nix_FilterItem, CBC_ORDER_ANY); MUTATOR_HOOK(OnEntityPreSpawn, nix_OnEntityPreSpawn, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerPreThink, nix_PlayerPreThink, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerSpawn, nix_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(SetModname, nix_SetModname, CBC_ORDER_LAST); MUTATOR_ONADD { @@ -253,28 +245,6 @@ MUTATOR_DEFINITION(mutator_nix) nix_nextweapon = 0; NIX_precache(); - - FOR_EACH_PLAYER(e) - { - if(e.deadflag == DEAD_NO) - { - e.nix_save_cells = e.ammo_cells; - e.nix_save_shells = e.ammo_shells; - e.nix_save_nails = e.ammo_nails; - e.nix_save_rockets = e.ammo_rockets; - e.nix_save_fuel = e.ammo_fuel; - e.nix_save_weapons = e.weapons; - } - else - { - e.nix_save_cells = 0; - e.nix_save_shells = 0; - e.nix_save_nails = 0; - e.nix_save_rockets = 0; - e.nix_save_fuel = 0; - e.nix_save_weapons = 0; - } - } } MUTATOR_ONREMOVE @@ -283,12 +253,12 @@ MUTATOR_DEFINITION(mutator_nix) FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO) { - e.ammo_cells = max(start_ammo_cells, e.nix_save_cells); - e.ammo_shells = max(start_ammo_shells, e.nix_save_shells); - e.ammo_nails = max(start_ammo_nails, e.nix_save_nails); - e.ammo_rockets = max(start_ammo_rockets, e.nix_save_rockets); - e.ammo_fuel = max(start_ammo_fuel, e.nix_save_fuel); - e.weapons = (start_weapons | e.nix_save_weapons); + e.ammo_cells = start_ammo_cells; + e.ammo_shells = start_ammo_shells; + e.ammo_nails = start_ammo_nails; + e.ammo_rockets = start_ammo_rockets; + e.ammo_fuel = start_ammo_fuel; + WEPSET_COPY_EA(e, start_weapons); if(!client_hasweapon(e, e.weapon, TRUE, FALSE)) e.switchweapon = w_getbestweapon(self); } diff --git a/qcsrc/server/mutators/mutator_superspec.qc b/qcsrc/server/mutators/mutator_superspec.qc new file mode 100644 index 0000000000..8b6bf05a20 --- /dev/null +++ b/qcsrc/server/mutators/mutator_superspec.qc @@ -0,0 +1,530 @@ +#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" +#define _ISLOCAL ((edict_num(1) == self) ? TRUE : FALSE) + +#define ASF_STRENGTH 1 +#define ASF_SHIELD 2 +#define ASF_MEGA_AR 4 +#define ASF_MEGA_HP 8 +#define ASF_FLAG_GRAB 16 +#define ASF_OBSERVER_ONLY 32 +#define ASF_SHOWWHAT 64 +#define ASF_SSIM 128 +#define ASF_ALL 0xFFFFFF +.float autospec_flags; + +#define SSF_SILENT 1 +#define SSF_VERBOSE 2 +#define SSF_ITEMMSG 4 +.float superspec_flags; + +.string superspec_itemfilter; //"classname1 classname2 ..." + +float _spectate(entity _player) +{ + if(SpectateNext(_player) == 1) + { + PutObserverInServer(); + self.classname = "spectator"; + } + + return TRUE; +} + +void superspec_save_client_conf() +{ + string fn = "superspec-local.options"; + float fh; + + + if not(_ISLOCAL) + { + if(self.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); + } + + fh = fopen(fn, FILE_WRITE); + if(fh < 0) + { + dprint("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n"); + } + else + { + fputs(fh, _SSMAGIX); + fputs(fh, "\n"); + fputs(fh, ftos(self.autospec_flags)); + fputs(fh, "\n"); + fputs(fh, ftos(self.superspec_flags)); + fputs(fh, "\n"); + fputs(fh, self.superspec_itemfilter); + fputs(fh, "\n"); + fclose(fh); + } +} + +void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) +{ + + sprint(_to, strcat(_con_title, _msg)); + + if(_to.superspec_flags & SSF_SILENT) + return; + + if(_spamlevel > 1) + if not(_to.superspec_flags & SSF_VERBOSE) + return; + + centerprint(_to, strcat(_center_title, _msg)); +} + +float superspec_filteritem(entity _for, entity _item) +{ + float i; + + if(!_for.superspec_itemfilter) + return TRUE; + + if(_for.superspec_itemfilter == "") + return TRUE; + + float l = tokenize_console(_for.superspec_itemfilter); + for(i = 0; i < l; ++i) + { + if(argv(i) == _item.classname) + return TRUE; + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(superspec_ItemTouch) +{ + entity _oldself = self; + entity _item = self; + + FOR_EACH_SPEC(self) + { + if(self.superspec_flags & SSF_ITEMMSG) + if(superspec_filteritem(self, _item)) + { + if(self.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1); + else + superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1); + if(self.autospec_flags& ASF_SSIM && self.enemy != other) + { + _spectate(other); + + self = _oldself; + return FALSE; + } + } + + + if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) || + (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) || + (self.autospec_flags& ASF_MEGA_AR && _item.classname == "item_armor_large") || + (self.autospec_flags& ASF_MEGA_HP && _item.classname == "item_health_mega") || + (self.autospec_flags& ASF_FLAG_GRAB && _item.classname == "item_flag_team")) + { + + if((self.enemy != other) || self.classname == "observer") + { + if(self.autospec_flags & ASF_OBSERVER_ONLY && self.classname != "observer") + { + if(self.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", self, sprintf("^8Ignored that %s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2); + } + else + { + if(self.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2); + + _spectate(other); + } + } + } + } + + self = _oldself; + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(superspec_SV_ParseClientCommand) +{ +#define OPTIONINFO(flag,var,test,text,long,short) \ + var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ + var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") + + if(MUTATOR_RETURNVALUE) // command was already handled? + return FALSE; + + if(self.classname == "player") + return FALSE; + + if(cmd_name == "superspec_itemfilter") + { + if(argv(1) == "help") + { + string _aspeco; + _aspeco = strcat(_aspeco, "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"); + _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); + _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); + superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1); + } + else if(argv(1) == "clear") + { + if(self.superspec_itemfilter != "") + strunzone(self.superspec_itemfilter); + + self.superspec_itemfilter = ""; + } + else if(argv(1) == "show" || argv(1) == "") + { + if(self.superspec_itemfilter == "") + { + superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1); + return TRUE; + } + float i; + float l = tokenize_console(self.superspec_itemfilter); + string _msg; + for(i = 0; i < l; ++i) + _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); + //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); + + _msg = strcat(_msg,"\n"); + + superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1); + } + else + { + if(self.superspec_itemfilter != "") + strunzone(self.superspec_itemfilter); + + self.superspec_itemfilter = strzone(argv(1)); + } + + + return TRUE; + } + + if(cmd_name == "superspec") + { + string _aspeco; + + if(cmd_argc > 1) + { + float i, _bits, _start = 1; + if(argv(1) == "help") + { + _aspeco = ""; + _aspeco = strcat(_aspeco, "use cmd superspec [option] [on|off] to set options\n\n"); + _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supress ALL mesagess from superspectate.\n"); + _aspeco = strcat(_aspeco, "^3 verrbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); + _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that was picked up.\n"); + _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); + superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1); + return TRUE; + } + + if(argv(1) == "clear") + { + self.superspec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + self.superspec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + self.superspec_flags &~= _bits; + + _bits = 0; + } + else + { + if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; + if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; + if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; + } + } + } + + + OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); + OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); + OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); + + superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1); + return TRUE; + + } + +///////////////////// + + if(cmd_name == "autospec") + { + string _aspeco; + if(cmd_argc > 1) + { + if(argv(1) == "help") + { + _aspeco = ""; + _aspeco = strcat(_aspeco, "use cmd autospec [option] [on|off] to set options\n\n"); + _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); + _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); + _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); + _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); + _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); + _aspeco = strcat(_aspeco, "^3 observer_only (short^5 oo^7) for automatic spectate only if in observer mode\n"); + _aspeco = strcat(_aspeco, "^3 show_what (short^5 sw^7) to display what event triggerd autospectate\n"); + _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggerd\n"); + _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) turn everything on/off\n"); + superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1); + return TRUE; + } + + float i, _bits, _start = 1; + if(argv(1) == "clear") + { + self.autospec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + self.autospec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + self.autospec_flags &~= _bits; + + _bits = 0; + } + else + { + if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; + if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; + if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; + if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; + if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; + if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; + if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; + if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; + if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; + } + } + } + + OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if Observer", "observer_only", "oo"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); + + superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1); + return TRUE; + } + + if(cmd_name == "followpowerup") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.strength_finished > time || _player.invincible_finished > time) + return _spectate(_player); + } + + superspec_msg("", "", self, "No active powerups\n", 1); + return TRUE; + } + + if(cmd_name == "followstrength") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.strength_finished > time) + return _spectate(_player); + } + + superspec_msg("", "", self, "No active Strength\n", 1); + return TRUE; + } + + if(cmd_name == "followstshield") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.invincible_finished > time) + return _spectate(_player); + } + + superspec_msg("", "", self, "No active Shield\n", 1); + return TRUE; + } + + if(cmd_name == "followfc") + { + if(!g_ctf) + return TRUE; + + entity _player; + float _team; + + if(cmd_argc == 2) + { + if(argv(1) == "red") + _team = COLOR_TEAM1; + else + _team = COLOR_TEAM2; + } + + FOR_EACH_PLAYER(_player) + { + if(_player.flagcarried && (_player.team == _team || _team == 0)) + return _spectate(_player); + } + + superspec_msg("", "", self, "No active flag carrier\n", 1); + return TRUE; + } + + return FALSE; +#undef OPTIONINFO +} + +MUTATOR_HOOKFUNCTION(superspec_BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":SS"); + return 0; +} + +MUTATOR_HOOKFUNCTION(superspec_BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Super Spectators"); + return 0; +} + +/* +MUTATOR_HOOKFUNCTION(superspec_PlayerSpawn) +{ + + return FALSE; +} +*/ + +void superspec_hello() +{ + if(self.enemy.crypto_idfp == "") + centerprint(self.enemy, "Your client have/allow no crypto id, superspec options will not be saved/restored."); + else + centerprint(self.enemy, sprintf("Hello %s\nSince your client has a Crypto ID, your superspec preferenses will be presisted on this server.", self.enemy.netname)); + + remove(self); +} + +MUTATOR_HOOKFUNCTION(superspec_ClientConnect) +{ + string fn = "superspec-local.options"; + float fh; + + self.superspec_flags = SSF_VERBOSE; + self.superspec_itemfilter = ""; + + entity _hello = spawn(); + _hello.enemy = self; + _hello.think = superspec_hello; + _hello.nextthink = time + 5; + + if not(_ISLOCAL) + { + if(self.crypto_idfp == "") + return FALSE; + + fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); + } + + fh = fopen(fn, FILE_READ); + if(fh < 0) + { + dprint("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n"); + } + else + { + string _magic = fgets(fh); + if(_magic != _SSMAGIX) + { + dprint("^1ERROR^7 While reading superspec options file: unkown magic\n"); + } + else + { + self.autospec_flags = stof(fgets(fh)); + self.superspec_flags = stof(fgets(fh)); + self.superspec_itemfilter = strzone(fgets(fh)); + } + fclose(fh); + } + + return FALSE; +} + +MUTATOR_HOOKFUNCTION(superspec_ClientDisconnect) +{ + superspec_save_client_conf(); + return FALSE; +} + + +/* +MUTATOR_HOOKFUNCTION(superspec_MakePlayerObserver) +{ + return FALSE; +} + +MUTATOR_HOOKFUNCTION(superspec_PlayerPreThink) +{ + return FALSE; +} +*/ + +MUTATOR_DEFINITION(mutator_superspec) +{ + + MUTATOR_HOOK(BuildMutatorsString, superspec_BuildMutatorsString, CBC_ORDER_ANY); + MUTATOR_HOOK(BuildMutatorsPrettyString, superspec_BuildMutatorsPrettyString, CBC_ORDER_ANY); + MUTATOR_HOOK(SV_ParseClientCommand, superspec_SV_ParseClientCommand, CBC_ORDER_ANY); + MUTATOR_HOOK(ItemTouch, superspec_ItemTouch, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientConnect, superspec_ClientConnect, CBC_ORDER_ANY); + //MUTATOR_HOOK(PlayerSpawn, superspec_PlayerSpawn, CBC_ORDER_ANY); + //MUTATOR_HOOK(PlayerPreThink, superspec_PlayerPreThink, CBC_ORDER_ANY); + //MUTATOR_HOOK(MakePlayerObserver, superspec_MakePlayerObserver, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, superspec_ClientDisconnect, CBC_ORDER_ANY); + + MUTATOR_ONADD + { + } + + MUTATOR_ONREMOVE + { + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator_vampire.qc b/qcsrc/server/mutators/mutator_vampire.qc index 7e253ddf9d..40a925b3ce 100644 --- a/qcsrc/server/mutators/mutator_vampire.qc +++ b/qcsrc/server/mutators/mutator_vampire.qc @@ -10,7 +10,7 @@ MUTATOR_HOOKFUNCTION(vampire_PlayerDamage) else { // otherwise: each hit gets damage back - frag_attacker.health += damage_take; + frag_attacker.health = frag_attacker.health + bound(0, damage_take, self.health); } return 0; } diff --git a/qcsrc/server/mutators/mutators.qh b/qcsrc/server/mutators/mutators.qh index bc3c3fbd44..2ac6094d33 100644 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@ -1,14 +1,17 @@ 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_new_toys); MUTATOR_DECLARATION(mutator_nix); -MUTATOR_DECLARATION(mutator_dodging); MUTATOR_DECLARATION(mutator_rocketflying); -MUTATOR_DECLARATION(mutator_vampire); -MUTATOR_DECLARATION(mutator_spawn_near_teammate); MUTATOR_DECLARATION(mutator_spawn_near_teammate); +MUTATOR_DECLARATION(mutator_vampire); +MUTATOR_DECLARATION(mutator_superspec); MUTATOR_DECLARATION(sandbox); diff --git a/qcsrc/server/mutators/sandbox.qc b/qcsrc/server/mutators/sandbox.qc index 31a1a4e854..d7fec13264 100644 --- a/qcsrc/server/mutators/sandbox.qc +++ b/qcsrc/server/mutators/sandbox.qc @@ -205,7 +205,7 @@ string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't def string sandbox_ObjectPort_Save(entity e, float database) { // save object properties, and return them as a string - float i; + float i = 0; string s; entity head; @@ -268,6 +268,7 @@ string sandbox_ObjectPort_Save(entity e, float database) } // now apply the array to a simple string, with the ; symbol separating objects + s = ""; for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) { if(port_string[i]) @@ -744,13 +745,16 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); return TRUE; case "mesh": + s = ""; FOR_EACH_TAG(e) s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); return TRUE; case "attachments": // this should show the same info as 'mesh' but for attachments + s = ""; entity head; + i = 0; for(head = world; (head = find(head, classname, "object")); ) { if(head.owner == e) diff --git a/qcsrc/server/pathlib.qc b/qcsrc/server/pathlib.qc index 95aa2f9658..ce4c2b4b8d 100644 --- a/qcsrc/server/pathlib.qc +++ b/qcsrc/server/pathlib.qc @@ -598,6 +598,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go { pathlib_expandnode = pathlib_expandnode_box; pathlib_movenode = pathlib_swimnode; + doedge = 0; } else { @@ -605,6 +606,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go { pathlib_expandnode = pathlib_expandnode_box; pathlib_movenode = pathlib_swimnode; + doedge = 0; } else { diff --git a/qcsrc/server/pathlib/expandnode.qc b/qcsrc/server/pathlib/expandnode.qc index 4647fea139..a08c66b3ae 100644 --- a/qcsrc/server/pathlib/expandnode.qc +++ b/qcsrc/server/pathlib/expandnode.qc @@ -49,6 +49,7 @@ float pathlib_expandnode_starf(entity node, vector start, vector goal) plib_points2[0] = plib_points[0]; vector bp; bp = plib_points[0]; + fc2 = 0; for(i = 0; i < 8; ++i) { c = 0; diff --git a/qcsrc/server/pathlib/utility.qc b/qcsrc/server/pathlib/utility.qc index 5d9fc3d978..0a5ea42b1c 100644 --- a/qcsrc/server/pathlib/utility.qc +++ b/qcsrc/server/pathlib/utility.qc @@ -156,7 +156,7 @@ float tile_check_plus(vector where) float tile_check_plus2(vector where) { vector p,f,r; - float i,e; + float i = 0, e = 0; f = PLIB_FORWARD * pathlib_gridsize; r = PLIB_RIGHT * pathlib_gridsize; diff --git a/qcsrc/server/playerstats.qc b/qcsrc/server/playerstats.qc index 3533b4882e..4608b56df1 100644 --- a/qcsrc/server/playerstats.qc +++ b/qcsrc/server/playerstats.qc @@ -5,7 +5,7 @@ string events_last; .float playerstats_addedglobalinfo; .string playerstats_id; -void PlayerStats_Init() +void PlayerStats_Init() // initiated before InitGameplayMode so that scores are added properly { string uri; playerstats_db = -1; @@ -20,6 +20,7 @@ void PlayerStats_Init() serverflags |= SERVERFLAG_PLAYERSTATS; PlayerStats_AddEvent(PLAYERSTATS_ALIVETIME); + PlayerStats_AddEvent(PLAYERSTATS_AVGLATENCY); PlayerStats_AddEvent(PLAYERSTATS_WINS); PlayerStats_AddEvent(PLAYERSTATS_MATCHES); PlayerStats_AddEvent(PLAYERSTATS_JOINS); @@ -174,7 +175,7 @@ void PlayerStats_TeamScore(float t, string event_id, float value) // TODO: doesn The following keys are defined: - V: format version (always 1) - this MUST be the first line! + V: format version (always a fixed number) - this MUST be the first line! #: comment (MUST be ignored by any parser) R: release information on the server T: time at which the game ended @@ -189,22 +190,42 @@ void PlayerStats_TeamScore(float t, string event_id, float value) // TODO: doesn P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!) n: nickname of the player (optional) t: team ID + i: player index 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) scoreboardvalid: set to 1 if the player was there at the end of the match total-: total score of that scoreboard item scoreboard-: end-of-game score of that scoreboard item (can differ in non-team games) - achievement-: achievement counters + achievement-: achievement counters (their "count" is usually 1 if nonzero at all) + kills-: number of kills against the indexed player rank : rank of player acc--hit: total damage dealt acc--fired: total damage that all fired projectiles *could* have dealt acc--cnt-hit: amount of shots that actually hit acc--cnt-fired: amount of fired shots acc--frags: amount of frags dealt by weapon + + Response format (not used yet): + + V: format version (always 1) - this MUST be the first line! + #: comment (MUST be ignored by any parser) + R: release information on the XonStat server + T: current time + S: in case of a stats submit request, the human readable xonstat URL for the submitted match + P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!) + e: followed by an event name, a space, and the event count/score, and - if this is a reply to a stats submit request - a space, and the delta of the event count/score caused by this match + event names can be the same as above (they then are either sums, or minimum/maximum values, depending on context), as well as: + elo: current Elo calculated by the stats server + rank : global rank of player for this game type (for stats submit requests) + rank- : global rank of player for any game type (for non stats submit requests) + not all events need to be included, of course + if an event is counted additively from unprocessed submitted data, it should not be sent as part of stats submit response + achievement- events may be generated by the xonstat server and reported as part of stats submit responses! */ void PlayerStats_ready(entity fh, entity pass, float status) @@ -217,7 +238,7 @@ void PlayerStats_ready(entity fh, entity pass, float status) switch(status) { case URL_READY_CANWRITE: - url_fputs(fh, "V 1\n"); + url_fputs(fh, "V 5\n"); #ifdef WATERMARK url_fputs(fh, sprintf("R %s\n", WATERMARK())); #endif @@ -325,6 +346,7 @@ void PlayerStats_Accuracy(entity p) PlayerStats_Event(p, strcat("acc-", w.netname, "-frags"), a.(accuracy_frags[i-1])); } + //backtrace(strcat("adding player stat accuracy for ", p.netname, ".\n")); } void PlayerStats_AddGlobalInfo(entity p) @@ -365,17 +387,14 @@ void PlayerStats_EndMatch(float finished) winner = PlayerScore_Sort(score_dummyfield); FOR_EACH_CLIENT(p) // spectators intentionally not included { - PlayerStats_Accuracy(p); - if(g_arena || g_lms || g_ca) - { - if(p.alivetime <= 0) - continue; - } - else - { - if(p.classname != "player") - continue; - } + //PlayerStats_Accuracy(p); // stats are already written with PlayerStats_AddGlobalInfo(entity), don't double them up. + + if((g_arena || g_lms || g_ca) && (p.alivetime <= 0)) { continue; } + else if(p.classname != "player") { continue; } + + float latency = (p.latency_sum / p.latency_cnt); + if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); } + PlayerScore_PlayerStats(p); PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_VALID, 1); if(finished) diff --git a/qcsrc/server/playerstats.qh b/qcsrc/server/playerstats.qh index b99366434b..11a311028d 100644 --- a/qcsrc/server/playerstats.qh +++ b/qcsrc/server/playerstats.qh @@ -1,5 +1,6 @@ // time the player was alive and kicking string PLAYERSTATS_ALIVETIME = "alivetime"; +string PLAYERSTATS_AVGLATENCY = "avglatency"; string PLAYERSTATS_WINS = "wins"; string PLAYERSTATS_MATCHES = "matches"; string PLAYERSTATS_JOINS = "joins"; diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index 76af253dca..75dae14bf5 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -154,8 +154,11 @@ float Portal_TeleportPlayer(entity teleporter, entity player) // factor -1 allows chaining portals, but may be weird player.right_vector = -1 * AnglesTransform_Apply(transform, player.right_vector); - if(player.flagcarried) - DropFlag(player.flagcarried, player, world); + entity oldself = self; + self = player; + MUTATOR_CALLHOOK(PortalTeleport); + player = self; + self = oldself; if not(teleporter.enemy) { diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 0dbe537818..028519372f 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -29,7 +29,9 @@ defs.qh // Should rename this, it has fields and globals mutators/base.qh mutators/mutators.qh +mutators/gamemode_ctf.qh mutators/gamemode_keyhunt.qh // TODO fix this +mutators/gamemode_keepaway.qh mutators/gamemode_nexball.qh mutators/mutator_dodging.qh @@ -138,9 +140,9 @@ cl_client.qc t_plats.qc antilag.qc -ctf.qc +//ctf.qc domination.qc -mode_onslaught.qc +//mode_onslaught.qc //nexball.qc g_hook.qc @@ -205,17 +207,21 @@ playerstats.qc ../common/explosion_equation.qc mutators/base.qc -mutators/gamemode_nexball.qc -mutators/gamemode_keyhunt.qc +mutators/gamemode_ctf.qc mutators/gamemode_freezetag.qc +mutators/gamemode_keyhunt.qc mutators/gamemode_keepaway.qc +mutators/gamemode_nexball.qc +mutators/gamemode_onslaught.qc mutators/mutator_invincibleproj.qc +mutators/mutator_new_toys.qc mutators/mutator_nix.qc mutators/mutator_dodging.qc mutators/mutator_rocketflying.qc mutators/mutator_vampire.qc mutators/mutator_spawn_near_teammate.qc mutators/sandbox.qc +mutators/mutator_superspec.qc ../warpzonelib/anglestransform.qc ../warpzonelib/mathlib.qc diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index 911106a305..c7c38165dd 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -141,6 +141,7 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) newpos = race_readPos(map, t); float i; + player_prevpos = 0; for(i = 1; i <= RANKINGS_CNT; ++i) { if(race_readUID(map, i) == myuid) @@ -490,8 +491,8 @@ void checkpoint_passed() self.race_checkpoint = other.race_checkpoint; } - float largest_cp_id; - float cp_amount; + float largest_cp_id = 0; + float cp_amount = 0; for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { cp_amount += 1; if(cp.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint @@ -664,7 +665,7 @@ void trigger_race_checkpoint_verify() g_race_qualifying = 1; self.race_place = race_lowest_place_spawn; if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE)) - error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for qualifying) - bailing out")); + error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(self.race_place), " (used for qualifying) - bailing out")); } else { @@ -710,7 +711,7 @@ void trigger_race_checkpoint_verify() } if(defragcpexists != -1){ - float largest_cp_id; + float largest_cp_id = 0; for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) if(cp.race_checkpoint > largest_cp_id) largest_cp_id = cp.race_checkpoint; diff --git a/qcsrc/server/runematch.qc b/qcsrc/server/runematch.qc index 8f5e005a42..239afb04a8 100644 --- a/qcsrc/server/runematch.qc +++ b/qcsrc/server/runematch.qc @@ -133,7 +133,8 @@ string RuneName(float r) vector RuneColormod(float r) { - vector _color; + vector _color = '255 0 255'; + if(r == RUNE_STRENGTH) _color = '255 0 0'; if(r == RUNE_DEFENSE) @@ -178,6 +179,7 @@ void RuneCarriedThink() // count runes my owner holds rcount = 0; rune = find(world, classname, "rune"); + rnum = -1; while(rune) { if(rune.owner == self.owner) @@ -341,7 +343,6 @@ void DropAllRunes(entity pl) entity curse1, rune1, curse2, rune2; - rune = curse = world; rcount = ccount = r = c = 0; rune = find(rune, classname, "rune"); while(rune) @@ -361,7 +362,6 @@ void DropAllRunes(entity pl) numtodrop = autocvar_g_runematch_drop_runes_max; prevent_same = !autocvar_g_runematch_allow_same; - rune = curse = world; do { rune = find(rune, classname, "rune"); @@ -588,6 +588,7 @@ float RunematchHandleFrags(entity attacker, entity targ, float f) if(!arunes && !trunes) return f - 1 + autocvar_g_runematch_frags_norune; // don't give points to players when no runes are involved. + newfrags = 0; if(arunes) { // got a kill while holding runes newfrags = newfrags + autocvar_g_runematch_frags_killedby_runeholder;//5; diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index 10f5d12fcd..102dd6a33e 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -127,7 +127,7 @@ float TeamScore_Compare(entity t1, entity t2) { if(!t1 || !t2) return (!t2) - !t1; - vector result; + vector result = '0 0 0'; float i; for(i = 0; i < MAX_TEAMSCORE; ++i) { @@ -356,7 +356,7 @@ float PlayerScore_Compare(entity t1, entity t2) { if(!t1 || !t2) return (!t2) - !t1; - vector result; + vector result = '0 0 0'; float i; for(i = 0; i < MAX_SCORE; ++i) { diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index 806e245079..c4021fc397 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -44,26 +44,6 @@ void ScoreRules_generic() ScoreRules_basics_end(); } -// g_ctf -#define ST_CTF_CAPS 1 -#define SP_CTF_CAPS 4 -#define SP_CTF_PICKUPS 5 -#define SP_CTF_DROPS 6 -#define SP_CTF_FCKILLS 7 -#define SP_CTF_RETURNS 8 -void ScoreRules_ctf() -{ - CheckAllowedTeams(world); - ScoreRules_basics(2 + (c3>=0), SFL_SORT_PRIO_PRIMARY, 0, TRUE); // NOTE this assumes that the rogue team is team 3 - ScoreInfo_SetLabel_TeamScore (ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS, "pickups", 0); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS, "fckills", 0); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS, "returns", 0); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER); - ScoreRules_basics_end(); -} - // g_domination #define ST_DOM_TICKS 1 #define SP_DOM_TICKS 4 @@ -169,19 +149,6 @@ void ScoreRules_nexball(float teams) ScoreRules_basics_end(); } -// Keep Away stuff -#define SP_KEEPAWAY_PICKUPS 4 -#define SP_KEEPAWAY_CARRIERKILLS 5 -#define SP_KEEPAWAY_BCTIME 6 -void ScoreRules_keepaway() -{ - ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, TRUE); // SFL_SORT_PRIO_PRIMARY - ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS, "pickups", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); - ScoreRules_basics_end(); -} - // FreezeTag stuff #define SP_FREEZETAG_REVIVALS 4 void ScoreRules_freezetag() diff --git a/qcsrc/server/steerlib.qc b/qcsrc/server/steerlib.qc index a586252555..2f59924df6 100644 --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@ -172,8 +172,8 @@ vector steerlib_dodge(vector point,vector dodge_dir,float min_distance) vector steerlib_flock(float radius, float standoff,float separation_force,float flock_force) { entity flock_member; - vector push,pull; - float ccount; + vector push = '0 0 0', pull = '0 0 0'; + float ccount = 0; flock_member = findradius(self.origin,radius); while(flock_member) @@ -198,8 +198,8 @@ vector steerlib_flock(float radius, float standoff,float separation_force,float vector steerlib_flock2d(float radius, float standoff,float separation_force,float flock_force) { entity flock_member; - vector push,pull; - float ccount; + vector push = '0 0 0', pull = '0 0 0'; + float ccount = 0; flock_member = findradius(self.origin,radius); while(flock_member) @@ -229,8 +229,8 @@ vector steerlib_flock2d(float radius, float standoff,float separation_force,floa vector steerlib_swarm(float radius, float standoff,float separation_force,float swarm_force) { entity swarm_member; - vector force,center; - float ccount; + vector force = '0 0 0', center = '0 0 0'; + float ccount = 0; swarm_member = findradius(self.origin,radius); diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 007c6369ed..63d91f6070 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -231,8 +231,6 @@ void StartFrame (void) CreatureFrame (); CheckRules_World (); - AuditTeams(); - RuneMatchGivePoints(); bot_serverframe(); diff --git a/qcsrc/server/t_items.qc b/qcsrc/server/t_items.qc index b26c840ff0..48b4fdd31c 100644 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@ -1,3 +1,274 @@ +#define ISF_LOCATION 2 +#define ISF_MODEL 4 +#define ISF_STATUS 8 + #define ITS_STAYWEP 1 + #define ITS_ANIMATE1 2 + #define ITS_ANIMATE2 4 + #define ITS_AVAILABLE 8 + #define ITS_ALLOWFB 16 + #define ITS_ALLOWSI 32 + #define ITS_POWERUP 64 +#define ISF_COLORMAP 16 +#define ISF_DROP 32 +#define ISF_ANGLES 64 + +.float ItemStatus; + +#ifdef CSQC + +var float autocvar_cl_animate_items = 1; +var float autocvar_cl_ghost_items = 0.45; +var vector autocvar_cl_ghost_items_color = '-1 -1 -1'; +var float autocvar_cl_fullbright_items = 0; +var vector autocvar_cl_weapon_stay_color = '2 0.5 0.5'; +var float autocvar_cl_weapon_stay_alpha = 0.75; +var float autocvar_cl_simple_items = 0; +var string autocvr_cl_simpleitems_postfix = "_simple"; +.float spawntime; +.float gravity; +.vector colormod; +void ItemDraw() +{ + if(self.gravity) + { + 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); + + if(self.move_flags & FL_ONGROUND) + self.gravity = 0; + } +} + +void ItemRead(float _IsNew) +{ + float sf = ReadByte(); + + if(sf & ISF_LOCATION) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + 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(); + + if(self.ItemStatus & ITS_AVAILABLE) + { + self.alpha = 1; + self.colormod = self.glowmod = '1 1 1'; + } + else + { + if (autocvar_cl_ghost_items_color) + { + self.alpha = autocvar_cl_ghost_items; + self.colormod = self.glowmod = autocvar_cl_ghost_items_color; + } + else + self.alpha = -1; + } + + if(autocvar_cl_fullbright_items) + if(self.ItemStatus & ITS_ALLOWFB) + self.effects |= EF_FULLBRIGHT; + + if(self.ItemStatus & ITS_STAYWEP) + { + self.colormod = self.glowmod = autocvar_cl_weapon_stay_color; + self.alpha = autocvar_cl_weapon_stay_alpha; + + } + + if(self.ItemStatus & ITS_POWERUP) + { + if(self.ItemStatus & ITS_AVAILABLE) + self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); + else + self.effects &~= (EF_ADDITIVE | EF_FULLBRIGHT); + } + } + + if(sf & ISF_MODEL) + { + self.drawmask = MASK_NORMAL; + self.movetype = MOVETYPE_NOCLIP; + self.draw = ItemDraw; + + if(self.mdl) + strunzone(self.mdl); + + self.mdl = ""; + string _fn = ReadString(); + + if(autocvar_cl_simple_items && (self.ItemStatus & ITS_ALLOWSI)) + { + string _fn2 = substring(_fn, 0 , strlen(_fn) -4); + self.draw = ItemDrawSimple; + + + + 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; + dprint("Simple item requested for ", _fn, " but no model exsist for it\n"); + } + } + + if(self.draw != ItemDrawSimple) + self.mdl = strzone(_fn); + + + if(self.mdl == "") + dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, " tell tZork aboute this!\n"); + + precache_model(self.mdl); + setmodel(self, self.mdl); + } + + if(sf & ISF_COLORMAP) + self.colormap = ReadShort(); + + 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(); + self.move_velocity_z = ReadCoord(); + self.velocity = self.move_velocity; + self.move_origin = self.oldorigin; + + if(!self.move_time) + { + self.move_time = time; + self.spawntime = time; + } + 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 +float autocvar_sv_simple_items; +float ItemSend(entity to, float sf) +{ + if(self.gravity) + sf |= ISF_DROP; + else + sf &~= ISF_DROP; + + WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM); + WriteByte(MSG_ENTITY, sf); + + //WriteByte(MSG_ENTITY, self.cnt); + if(sf & ISF_LOCATION) + { + WriteCoord(MSG_ENTITY, self.origin_x); + 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); + + if(sf & ISF_MODEL) + { + + if(self.mdl == "") + dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, "exspect a crash just aboute now\n"); + + WriteString(MSG_ENTITY, self.mdl); + } + + + if(sf & ISF_COLORMAP) + WriteShort(MSG_ENTITY, self.colormap); + + if(sf & ISF_DROP) + { + WriteCoord(MSG_ENTITY, self.velocity_x); + WriteCoord(MSG_ENTITY, self.velocity_y); + WriteCoord(MSG_ENTITY, self.velocity_z); + } + + return TRUE; +} + + float have_pickup_item(void) { // minstagib: only allow filtered items @@ -29,7 +300,7 @@ float have_pickup_item(void) if(g_ca) return FALSE; if(g_weaponarena) - if((self.weapons & WEPBIT_ALL) || (self.items & IT_AMMO)) + if(!WEPSET_EMPTY_E(self) || (self.items & IT_AMMO)) return FALSE; } return TRUE; @@ -78,12 +349,12 @@ string Item_CounterFieldName(float it) .float max_armorvalue; .float pickup_anyway; - +/* float Item_Customize() { if(self.spawnshieldtime) return TRUE; - if(self.weapons != (self.weapons & other.weapons)) + if(!WEPSET_CONTAINS_ALL_EE(other, self)) { self.colormod = '0 0 0'; self.glowmod = self.colormod; @@ -103,80 +374,63 @@ float Item_Customize() return FALSE; } } +*/ void Item_Show (entity e, float mode) -{ +{ e.effects &~= EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST; + e.ItemStatus &~= ITS_STAYWEP; if (mode > 0) { // make the item look normal, and be touchable e.model = e.mdl; e.solid = SOLID_TRIGGER; - e.colormod = '0 0 0'; - self.glowmod = self.colormod; - e.alpha = 0; - e.customizeentityforclient = func_null; - e.spawnshieldtime = 1; + e.ItemStatus |= ITS_AVAILABLE; } else if (mode < 0) { // hide the item completely e.model = string_null; e.solid = SOLID_NOT; - e.colormod = '0 0 0'; - self.glowmod = self.colormod; - e.alpha = 0; - e.customizeentityforclient = func_null; - e.spawnshieldtime = 1; + e.ItemStatus &~= ITS_AVAILABLE; } else if((e.flags & FL_WEAPON) && !(e.flags & FL_NO_WEAPON_STAY) && g_weapon_stay) { // make the item translucent and not touchable e.model = e.mdl; e.solid = SOLID_TRIGGER; // can STILL be picked up! - e.colormod = '0 0 0'; - self.glowmod = self.colormod; e.effects |= EF_STARDUST; - e.customizeentityforclient = Item_Customize; - e.spawnshieldtime = 0; // field indicates whether picking it up may give you anything other than the weapon - } - else if(g_ghost_items) - { - // make the item translucent and not touchable - e.model = e.mdl; - e.solid = SOLID_NOT; - e.colormod = stov(autocvar_g_ghost_items_color); - e.glowmod = e.colormod; - e.alpha = g_ghost_items; - e.customizeentityforclient = func_null; - - e.spawnshieldtime = 1; + e.ItemStatus |= (ITS_AVAILABLE | ITS_STAYWEP); } else { - // hide the item completely - e.model = string_null; + //setmodel(e, "null"); e.solid = SOLID_NOT; e.colormod = '0 0 0'; e.glowmod = e.colormod; - e.alpha = 0; - e.customizeentityforclient = func_null; - e.spawnshieldtime = 1; + e.ItemStatus &~= ITS_AVAILABLE; } - - if (e.items & (IT_STRENGTH | IT_INVINCIBLE)) - e.effects |= EF_ADDITIVE | EF_FULLBRIGHT; + + if (e.items & IT_STRENGTH || e.items & IT_INVINCIBLE) + e.ItemStatus |= ITS_POWERUP; + if (autocvar_g_nodepthtestitems) e.effects |= EF_NODEPTHTEST; - if (autocvar_g_fullbrightitems) - e.effects |= EF_FULLBRIGHT; + + + if (autocvar_g_fullbrightitems) + e.ItemStatus |= ITS_ALLOWFB; + + if (autocvar_sv_simple_items) + e.ItemStatus |= ITS_ALLOWSI; // relink entity (because solid may have changed) setorigin(e, e.origin); + e.SendFlags |= ISF_STATUS; } void Item_Respawn (void) @@ -209,7 +463,7 @@ void Item_RespawnCountdown (void) if(self.count == 1) { string name; - vector rgb; + vector rgb = '1 0 1'; name = string_null; if(g_minstagib) { @@ -265,7 +519,7 @@ void Item_RespawnCountdown (void) void Item_ScheduleRespawnIn(entity e, float t) { - if((e.flags & FL_POWERUP) || (e.weapons & WEPBIT_SUPERWEAPONS)) + if((e.flags & FL_POWERUP) || WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) { e.think = Item_RespawnCountdown; e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS); @@ -348,7 +602,6 @@ float Item_GiveTo(entity item, entity player) float pickedup; float it; float i; - entity e; // if nothing happens to player, just return without taking the item pickedup = FALSE; @@ -377,10 +630,8 @@ float Item_GiveTo(entity item, entity player) // else if(item.items == IT_CELLS) // AnnounceTo(player, "ammo"); - if (item.weapons & WEPBIT_MINSTANEX) + if (WEPSET_CONTAINS_EW(item, WEP_MINSTANEX)) W_GiveWeapon (player, WEP_MINSTANEX, item.netname); - if (item.ammo_cells) - player.ammo_cells = bound(player.ammo_cells, 999, player.ammo_cells + autocvar_g_minstagib_ammo_drop); player.health = 100; } @@ -416,7 +667,7 @@ float Item_GiveTo(entity item, entity player) pickedup = TRUE; // sound not available // AnnounceTo(player, "speed"); - player.invincible_finished = max(player.invincible_finished, time) + autocvar_g_balance_powerup_strength_time; + player.invincible_finished = max(player.invincible_finished, time) + autocvar_g_balance_powerup_invincible_time; } } else @@ -428,7 +679,7 @@ float Item_GiveTo(entity item, entity player) if (player.switchweapon == w_getbestweapon(player)) _switchweapon = TRUE; - if not(player.weapons & W_WeaponBit(player.switchweapon)) + if not(WEPSET_CONTAINS_EW(player, player.switchweapon)) _switchweapon = TRUE; pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL); @@ -440,14 +691,17 @@ float Item_GiveTo(entity item, entity player) pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR); if (item.flags & FL_WEAPON) - if ((it = item.weapons - (item.weapons & player.weapons)) || (item.spawnshieldtime && self.pickup_anyway)) { - pickedup = TRUE; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) + WEPSET_DECLARE_A(it); + WEPSET_COPY_AE(it, item); + WEPSET_ANDNOT_AE(it, player); + + if (!WEPSET_EMPTY_A(it) || (item.spawnshieldtime && self.pickup_anyway)) { - e = get_weaponinfo(i); - if(it & e.weapons) - W_GiveWeapon (player, e.weapon, item.netname); + pickedup = TRUE; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(WEPSET_CONTAINS_AW(it, i)) + W_GiveWeapon (player, i, item.netname); } } @@ -493,13 +747,17 @@ float Item_GiveTo(entity item, entity player) void Item_Touch (void) { entity e, head; - + // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky) - if (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + if(self.classname == "droppedweapon") { - remove(self); - return; + if (ITEM_TOUCH_NEEDKILL()) + { + remove(self); + return; + } } + if (other.classname != "player") return; if (other.deadflag) @@ -508,6 +766,8 @@ void Item_Touch (void) return; if (self.owner == other) return; + if(MUTATOR_CALLHOOK(ItemTouch)) + return; if (self.classname == "droppedweapon") { @@ -551,6 +811,7 @@ void Item_Touch (void) } } e = RandomSelection_chosen_ent; + } else e = self; @@ -571,7 +832,7 @@ void Item_Reset() if(self.waypointsprite_attached) WaypointSprite_Kill(self.waypointsprite_attached); - if((self.flags & FL_POWERUP) | (self.weapons & WEPBIT_SUPERWEAPONS)) // do not spawn powerups initially! + if((self.flags & FL_POWERUP) | WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) // do not spawn powerups initially! Item_ScheduleInitialRespawn(self); } } @@ -620,10 +881,10 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup float weapon_pickupevalfunc(entity player, entity item) { - float c, i, j, position; + float c, j, position; // See if I have it already - if(player.weapons & item.weapons == item.weapons) + if(!WEPSET_CONTAINS_ALL_EE(player, item)) { // If I can pick it up if(!item.spawnshieldtime) @@ -642,34 +903,27 @@ float weapon_pickupevalfunc(entity player, entity item) // If custom weapon priorities for bots is enabled rate most wanted weapons higher if( bot_custom_weapon && c ) { - for(i = WEP_FIRST; i <= WEP_LAST ; ++i) - { - // Find weapon - if( (get_weaponinfo(i)).weapons & item.weapons != item.weapons ) - continue; - - // Find the highest position on any range - position = -1; - for(j = 0; j < WEP_LAST ; ++j){ - if( - bot_weapons_far[j] == i || - bot_weapons_mid[j] == i || - bot_weapons_close[j] == i - ) - { - position = j; - break; - } - } - - // Rate it - if (position >= 0 ) + // Find the highest position on any range + position = -1; + for(j = 0; j < WEP_LAST ; ++j){ + if( + bot_weapons_far[j] == item.weapon || + bot_weapons_mid[j] == item.weapon || + bot_weapons_close[j] == item.weapon + ) { - position = WEP_LAST - position; - // item.bot_pickupbasevalue is overwritten here - return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c; + position = j; + break; } } + + // Rate it + if (position >= 0 ) + { + position = WEP_LAST - position; + // item.bot_pickupbasevalue is overwritten here + return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c; + } } return item.bot_pickupbasevalue * c; @@ -677,7 +931,8 @@ float weapon_pickupevalfunc(entity player, entity item) float commodity_pickupevalfunc(entity player, entity item) { - float c, i, need_shells, need_nails, need_rockets, need_cells, need_fuel; + float c, i; + float need_shells = FALSE, need_nails = FALSE, need_rockets = FALSE, need_cells = FALSE, need_fuel = FALSE; entity wi; c = 0; @@ -686,7 +941,7 @@ float commodity_pickupevalfunc(entity player, entity item) { wi = get_weaponinfo(i); - if not(wi.weapons & player.weapons) + if not(WEPSET_CONTAINS_EW(player, i)) continue; if(wi.items & IT_SHELLS) @@ -734,16 +989,50 @@ float commodity_pickupevalfunc(entity player, entity item) return item.bot_pickupbasevalue * c; } +void Item_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + RemoveItem(); +} .float is_item; void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue) { startitem_failed = FALSE; + if(self.model == "") + self.model = itemmodel; + + if(self.model == "") + { + error(strcat("^1Tried to spawn ", itemname, " with no model!\n")); + return; + } + + if(self.item_pickupsound == "") + self.item_pickupsound = pickupsound; + + if(!self.respawntime) // both need to be set + { + self.respawntime = defaultrespawntime; + self.respawntimejitter = defaultrespawntimejitter; + } + self.items = itemid; - self.weapons = weaponid; + self.weapon = weaponid; + + if(weaponid) + WEPSET_COPY_EW(self, weaponid); + self.flags = FL_ITEM | itemflags; + if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item + { + startitem_failed = TRUE; + remove(self); + return; + } + // is it a dropped weapon? if (self.classname == "droppedweapon") { @@ -755,10 +1044,13 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.think = RemoveItem; self.nextthink = time + 20; + self.takedamage = DAMAGE_YES; + self.event_damage = Item_Damage; + if(self.strength_finished || self.invincible_finished || self.superweapons_finished) /* if(self.items == 0) - if(self.weapons == (self.weapons & WEPBIT_SUPERWEAPONS)) // only superweapons + if(WEPSET_CONTAINS_ALL_AE(WEPBIT_SUPERWEAPONS, self)) // only superweapons if(self.ammo_nails == 0) if(self.ammo_cells == 0) if(self.ammo_rockets == 0) @@ -783,24 +1075,15 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, } else { - if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item - { - startitem_failed = TRUE; - remove(self); - return; - } - if(!have_pickup_item()) { startitem_failed = TRUE; remove (self); return; } - - if(self.model != "") - itemmodel = self.model; - if(self.item_pickupsound != "") - pickupsound = self.item_pickupsound; + + if(self.angles != '0 0 0') + self.SendFlags |= ISF_ANGLES; self.reset = Item_Reset; // it's a level item @@ -844,6 +1127,7 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, entity otheritem; for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain) { + // why not flags & fl_item? if(otheritem.is_item) { dprint("XXX Found duplicated item: ", itemname, vtos(self.origin)); @@ -854,10 +1138,10 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.is_item = TRUE; } - weaponsInMap |= weaponid; + WEPSET_OR_AW(weaponsInMap, weaponid); - precache_model (itemmodel); - precache_sound (pickupsound); + precache_model (self.model); + precache_sound (self.item_pickupsound); precache_sound ("misc/itemrespawncountdown.wav"); if(!g_minstagib && itemid == IT_STRENGTH) @@ -874,46 +1158,54 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, self.bot_pickup = TRUE; self.bot_pickupevalfunc = pickupevalfunc; self.bot_pickupbasevalue = pickupbasevalue; - self.mdl = itemmodel; - self.item_pickupsound = pickupsound; - if(self.weapons) - self.weapon = WEP_FIRST + log2of(lowestbit(self.weapons)); - else - self.weapon = 0; - // let mappers override respawntime - if(!self.respawntime) // both set - { - self.respawntime = defaultrespawntime; - self.respawntimejitter = defaultrespawntimejitter; - } + self.mdl = self.model; self.netname = itemname; self.touch = Item_Touch; - setmodel (self, self.mdl); // precision set below - self.effects |= EF_LOWPRECISION; + setmodel(self, "null"); // precision set below + //self.effects |= EF_LOWPRECISION; + if((itemflags & FL_POWERUP) || self.health || self.armorvalue) - setsize (self, '-16 -16 0', '16 16 48'); + { + self.pos1 = '-16 -16 0'; + self.pos2 = '16 16 48'; + } else - setsize (self, '-16 -16 0', '16 16 32'); + { + self.pos1 = '-16 -16 0'; + self.pos2 = '16 16 32'; + } + setsize (self, self.pos1, self.pos2); + + if(itemflags & FL_POWERUP) + self.ItemStatus |= ITS_ANIMATE1; + + if(self.armorvalue || self.health) + self.ItemStatus |= ITS_ANIMATE2; + if(itemflags & FL_WEAPON) - self.modelflags |= MF_ROTATE; - - if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely - if (itemflags & FL_WEAPON) { - // neutral team color for pickup weapons - self.colormap = 1024; // color shirt=0 pants=0 grey + if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely + self.colormap = 1024; // color shirt=0 pants=0 grey + else + self.gravity = 1; + + self.ItemStatus |= ITS_ANIMATE1; + self.ItemStatus |= ISF_COLORMAP; } self.state = 0; - if(self.team) + if(self.team) // broken, no idea why. { if(!self.cnt) self.cnt = 1; // item probability weight - self.effects = self.effects | EF_NODRAW; // marker for item team search + + self.effects |= EF_NODRAW; // marker for item team search InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET); } else Item_Reset(); + + Net_LinkEntity(self, FALSE, 0, ItemSend); } /* replace items in minstagib @@ -921,10 +1213,10 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, * IT_NAILS = extra lives * IT_INVINCIBLE = speed */ -void minstagib_items (float itemid) +void minstagib_items (float itemid) // will be deleted soon. { float rnd; - self.classname = "minstagib"; + self.classname = "minstagib"; // ...? // replace rocket launchers and nex guns with ammo cells if (itemid == IT_CELLS) @@ -995,8 +1287,23 @@ void weapon_defaultspawnfunc(float wpn) if(self.classname != "droppedweapon" && self.classname != "replacedweapon") { e = get_weaponinfo(wpn); - s = cvar_string(strcat("g_weaponreplace_", e.netname)); - if(s == "0") + + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + { + print("Attempted to spawn a mutator-blocked weapon; these guns will in the future require a mutator\n"); + /* + objerror("Attempted to spawn a mutator-blocked weapon rejected"); + startitem_failed = TRUE; + return; + */ + } + + s = W_Apply_Weaponreplace(e.netname); + ret_string = s; + other = e; + MUTATOR_CALLHOOK(SetWeaponreplace); + s = ret_string; + if(s == "") { remove(self); startitem_failed = TRUE; @@ -1029,7 +1336,7 @@ void weapon_defaultspawnfunc(float wpn) } self = oldself; } - if(t >= 1) + if(t >= 1) // always the case! { s = argv(0); wpn = 0; @@ -1059,7 +1366,7 @@ void weapon_defaultspawnfunc(float wpn) if(!self.respawntime) { - if(e.weapons & WEPBIT_SUPERWEAPONS) + if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) { self.respawntime = g_pickup_respawntime_superweapon; self.respawntimejitter = g_pickup_respawntimejitter_superweapon; @@ -1071,7 +1378,7 @@ void weapon_defaultspawnfunc(float wpn) } } - if(e.weapons & WEPBIT_SUPERWEAPONS) + if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) if(!self.superweapons_finished) self.superweapons_finished = autocvar_g_balance_superweapons_time; @@ -1095,14 +1402,19 @@ void weapon_defaultspawnfunc(float wpn) f = FL_WEAPON; // no weapon-stay on superweapons - if(e.weapons & WEPBIT_SUPERWEAPONS) + if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS)) f |= FL_NO_WEAPON_STAY; // weapon stay isn't supported for teamed weapons if(self.team) f |= FL_NO_WEAPON_STAY; - StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapons, f, weapon_pickupevalfunc, e.bot_pickupbasevalue); + // stupid minstagib hack, don't ask + if(g_minstagib) + if(self.ammo_cells) + self.ammo_cells = autocvar_g_minstagib_ammo_drop; + + StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue); if (self.modelindex) // don't precache if self was removed weapon_action(e.weapon, WR_PRECACHE); } @@ -1332,7 +1644,7 @@ void spawnfunc_item_invincible (void) { void spawnfunc_item_minst_cells (void) { if (g_minstagib) { - minst_no_auto_cells = 1; + minst_no_auto_cells = TRUE; minstagib_items(IT_CELLS); } else @@ -1405,19 +1717,21 @@ void spawnfunc_target_items (void) else if(argv(i) == "jetpack") self.items |= IT_JETPACK; else if(argv(i) == "fuel_regen") self.items |= IT_FUEL_REGEN; else - for(j = WEP_FIRST; j <= WEP_LAST; ++j) { - e = get_weaponinfo(j); - if(argv(i) == e.netname) + for(j = WEP_FIRST; j <= WEP_LAST; ++j) { - self.weapons |= e.weapons; - if(self.spawnflags == 0 || self.spawnflags == 2) - weapon_action(e.weapon, WR_PRECACHE); - break; + e = get_weaponinfo(j); + if(argv(i) == e.netname) + { + WEPSET_OR_EW(self, j); + if(self.spawnflags == 0 || self.spawnflags == 2) + weapon_action(e.weapon, WR_PRECACHE); + break; + } } + if(j > WEP_LAST) + print("target_items: invalid item ", argv(i), "\n"); } - if(j > WEP_LAST) - print("target_items: invalid item ", argv(i), "\n"); } string itemprefix, valueprefix; @@ -1462,8 +1776,8 @@ void spawnfunc_target_items (void) for(j = WEP_FIRST; j <= WEP_LAST; ++j) { e = get_weaponinfo(j); - if(e.weapons) - self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.weapons & e.weapons), e.netname); + if(e.weapon) + self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, WEPSET_CONTAINS_EW(self, j), e.netname); } } self.netname = strzone(self.netname); @@ -1524,6 +1838,36 @@ void spawnfunc_item_jetpack(void) #define OP_PLUS 3 #define OP_MINUS 4 +float GiveWeapon(entity e, float wpn, float op, float val) +{ + float v0, v1; + v0 = WEPSET_CONTAINS_EW(e, wpn); + switch(op) + { + case OP_SET: + if(val > 0) + WEPSET_OR_EW(e, wpn); + else + WEPSET_ANDNOT_EW(e, wpn); + break; + case OP_MIN: + case OP_PLUS: + if(val > 0) + WEPSET_OR_EW(e, wpn); + break; + case OP_MAX: + if(val <= 0) + WEPSET_ANDNOT_EW(e, wpn); + break; + case OP_MINUS: + if(val > 0) + WEPSET_ANDNOT_EW(e, wpn); + break; + } + v1 = WEPSET_CONTAINS_EW(e, wpn); + return (v0 != v1); +} + float GiveBit(entity e, .float fld, float bit, float op, float val) { float v0, v1; @@ -1604,7 +1948,9 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa e.regenfield = max(e.regenfield, time + regentime); } +#define PREGIVE_WEAPONS(e) WEPSET_DECLARE_A(save_weapons); WEPSET_COPY_AE(save_weapons, e) #define PREGIVE(e,f) float save_##f; save_##f = (e).f +#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), WEPSET_CONTAINS_AW(save_weapons, b), WEPSET_CONTAINS_EW(e, b), 0, snd_incr, snd_decr) #define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr) #define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) #define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) @@ -1631,7 +1977,7 @@ float GiveItems(entity e, float beginarg, float endarg) e.superweapons_finished = max(0, e.superweapons_finished - time); PREGIVE(e, items); - PREGIVE(e, weapons); + PREGIVE_WEAPONS(e); PREGIVE(e, strength_finished); PREGIVE(e, invincible_finished); PREGIVE(e, superweapons_finished); @@ -1684,8 +2030,9 @@ float GiveItems(entity e, float beginarg, float endarg) for(j = WEP_FIRST; j <= WEP_LAST; ++j) { wi = get_weaponinfo(j); - if(wi.weapons) - got += GiveBit(e, weapons, wi.weapons, op, val); + if(wi.weapon) + if not(wi.spawnflags & WEP_FLAG_MUTATORBLOCKED) + got += GiveWeapon(e, j, op, val); } case "allammo": got += GiveValue(e, ammo_cells, op, val); @@ -1746,7 +2093,7 @@ float GiveItems(entity e, float beginarg, float endarg) wi = get_weaponinfo(j); if(cmd == wi.netname) { - got += GiveBit(e, weapons, wi.weapons, op, val); + got += GiveWeapon(e, j, op, val); break; } } @@ -1765,11 +2112,11 @@ float GiveItems(entity e, float beginarg, float endarg) for(j = WEP_FIRST; j <= WEP_LAST; ++j) { wi = get_weaponinfo(j); - if(wi.weapons) + if(wi.weapon) { - POSTGIVE_BIT(e, weapons, wi.weapons, "weapons/weaponpickup.wav", string_null); - if not(save_weapons & wi.weapons) - if(e.weapons & wi.weapons) + POSTGIVE_WEAPON(e, j, "weapons/weaponpickup.wav", string_null); + if not(WEPSET_CONTAINS_AW(save_weapons, j)) + if(WEPSET_CONTAINS_EW(e, j)) weapon_action(wi.weapon, WR_PRECACHE); } } @@ -1784,7 +2131,7 @@ float GiveItems(entity e, float beginarg, float endarg) POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/megahealth.wav", string_null); if(e.superweapons_finished <= 0) - if(self.weapons & WEPBIT_SUPERWEAPONS) + if(WEPSET_CONTAINS_ANY_EA(self, WEPBIT_SUPERWEAPONS)) e.superweapons_finished = autocvar_g_balance_superweapons_time; if (g_minstagib) @@ -1806,10 +2153,11 @@ float GiveItems(entity e, float beginarg, float endarg) else e.superweapons_finished += time; - if not(e.weapons & W_WeaponBit(e.switchweapon)) + if not(WEPSET_CONTAINS_EW(e, e.switchweapon)) _switchweapon = TRUE; if(_switchweapon) W_SwitchWeapon_Force(e, w_getbestweapon(e)); return got; } +#endif diff --git a/qcsrc/server/t_jumppads.qc b/qcsrc/server/t_jumppads.qc index cce0aab32b..8923c19f18 100644 --- a/qcsrc/server/t_jumppads.qc +++ b/qcsrc/server/t_jumppads.qc @@ -2,6 +2,7 @@ float PUSH_ONCE = 1; float PUSH_SILENT = 2; .float pushltime; +.float istypefrag; .float height; void() SUB_UseTargets; @@ -213,6 +214,7 @@ void trigger_push_touch() // reset tracking of who pushed you into a hazard (for kill credit) other.pushltime = 0; + other.istypefrag = 0; } if(self.enemy.target) @@ -286,7 +288,7 @@ void trigger_push_findtarget() else if(n == 1) { // exactly one dest - bots love that - self.enemy = find(e, targetname, self.target); + self.enemy = find(world, targetname, self.target); } else { diff --git a/qcsrc/server/t_plats.qc b/qcsrc/server/t_plats.qc index 8d81ca263d..8c2766295e 100644 --- a/qcsrc/server/t_plats.qc +++ b/qcsrc/server/t_plats.qc @@ -1225,24 +1225,32 @@ entity spawn_field(vector fmins, vector fmaxs) } -float EntitiesTouching(entity e1, entity e2) +entity LinkDoors_nextent(entity cur, entity near, entity pass) { - if (e1.absmin_x > e2.absmax_x) + while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy)) + { + } + return cur; +} + +float LinkDoors_isconnected(entity e1, entity e2, entity pass) +{ + float DELTA = 4; + if (e1.absmin_x > e2.absmax_x + DELTA) return FALSE; - if (e1.absmin_y > e2.absmax_y) + if (e1.absmin_y > e2.absmax_y + DELTA) return FALSE; - if (e1.absmin_z > e2.absmax_z) + if (e1.absmin_z > e2.absmax_z + DELTA) return FALSE; - if (e1.absmax_x < e2.absmin_x) + if (e2.absmin_x > e1.absmax_x + DELTA) return FALSE; - if (e1.absmax_y < e2.absmin_y) + if (e2.absmin_y > e1.absmax_y + DELTA) return FALSE; - if (e1.absmax_z < e2.absmin_z) + if (e2.absmin_z > e1.absmax_z + DELTA) return FALSE; return TRUE; } - /* ============= LinkDoors @@ -1252,7 +1260,7 @@ LinkDoors */ void LinkDoors() { - entity t, starte; + entity t; vector cmins, cmaxs; if (self.enemy) @@ -1272,68 +1280,70 @@ void LinkDoors() return; // don't want to link this door } - cmins = self.absmin; - cmaxs = self.absmax; - - starte = self; - t = self; + FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world); - do + // set owner, and make a loop of the chain + dprint("LinkDoors: linking doors:"); + for(t = self; ; t = t.enemy) { - self.owner = starte; // master door - - if (self.health) - starte.health = self.health; - IFTARGETED - starte.targetname = self.targetname; - if (self.message != "") - starte.message = self.message; - - t = find(t, classname, self.classname); - if (!t) + dprint(" ", etos(t)); + t.owner = self; + if(t.enemy == world) { - self.enemy = starte; // make the chain a loop - - // shootable, or triggered doors just needed the owner/enemy links, - // they don't spawn a field - - self = self.owner; + t.enemy = self; + break; + } + } + dprint("\n"); - if (self.health) - return; - IFTARGETED - return; - if (self.items) - return; + // collect health, targetname, message, size + cmins = self.absmin; + cmaxs = self.absmax; + for(t = self; ; t = t.enemy) + { + if(t.health && !self.health) + self.health = t.health; + if(t.targetname && !self.targetname) + self.targetname = t.targetname; + if(t.message != "" && self.message == "") + self.message = t.message; + if (t.absmin_x < cmins_x) + cmins_x = t.absmin_x; + if (t.absmin_y < cmins_y) + cmins_y = t.absmin_y; + if (t.absmin_z < cmins_z) + cmins_z = t.absmin_z; + if (t.absmax_x > cmaxs_x) + cmaxs_x = t.absmax_x; + if (t.absmax_y > cmaxs_y) + cmaxs_y = t.absmax_y; + if (t.absmax_z > cmaxs_z) + cmaxs_z = t.absmax_z; + if(t.enemy == self) + break; + } - self.owner.trigger_field = spawn_field(cmins, cmaxs); + // distribute health, targetname, message + for(t = self; t; t = t.enemy) + { + t.health = self.health; + t.targetname = self.targetname; + t.message = self.message; + if(t.enemy == self) + break; + } - return; - } + // shootable, or triggered doors just needed the owner/enemy links, + // they don't spawn a field - if (EntitiesTouching(self,t)) - { - if (t.enemy) - objerror ("cross connected doors"); - - self.enemy = t; - self = t; - - if (t.absmin_x < cmins_x) - cmins_x = t.absmin_x; - if (t.absmin_y < cmins_y) - cmins_y = t.absmin_y; - if (t.absmin_z < cmins_z) - cmins_z = t.absmin_z; - if (t.absmax_x > cmaxs_x) - cmaxs_x = t.absmax_x; - if (t.absmax_y > cmaxs_y) - cmaxs_y = t.absmax_y; - if (t.absmax_z > cmaxs_z) - cmaxs_z = t.absmax_z; - } - } while (1 ); + if (self.health) + return; + IFTARGETED + return; + if (self.items) + return; + self.trigger_field = spawn_field(cmins, cmaxs); } @@ -1996,7 +2006,7 @@ void func_vectormamamam_findtarget() if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03) objerror("No reference entity found, so there is nothing to move. Aborting."); - self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0); + self.destvec = self.origin - func_vectormamamam_origin(self, 0); entity controller; controller = spawn(); diff --git a/qcsrc/server/t_quake3.qc b/qcsrc/server/t_quake3.qc index f415e1fd1d..7f8cb82921 100644 --- a/qcsrc/server/t_quake3.qc +++ b/qcsrc/server/t_quake3.qc @@ -22,9 +22,9 @@ void spawnfunc_ammo_lightning() { spawnfunc_item_cells(); } void spawnfunc_weapon_plasmagun() { spawnfunc_weapon_hagar(); } void spawnfunc_ammo_cells() { spawnfunc_item_rockets(); } -// Rail -> Rifle -void spawnfunc_weapon_railgun() { spawnfunc_weapon_rifle(); } -void spawnfunc_ammo_slugs() { spawnfunc_item_bullets(); } +// Rail -> Nex +void spawnfunc_weapon_railgun() { spawnfunc_weapon_nex(); } +void spawnfunc_ammo_slugs() { spawnfunc_item_cells(); } // BFG -> Crylink void spawnfunc_weapon_bfg() { spawnfunc_weapon_crylink(); } @@ -122,12 +122,8 @@ void spawnfunc_target_give() //void spawnfunc_item_health_mega() /* handled in t_items.qc */ //void spawnfunc_item_invis() /* not supported */ //void spawnfunc_item_regen() /* not supported */ -void spawnfunc_team_CTF_redflag() { spawnfunc_item_flag_team1(); } -void spawnfunc_team_CTF_blueflag() { spawnfunc_item_flag_team2(); } -void spawnfunc_team_CTF_redplayer() { spawnfunc_info_player_team1(); } -void spawnfunc_team_CTF_blueplayer() { spawnfunc_info_player_team2(); } -void spawnfunc_team_CTF_redspawn() { spawnfunc_info_player_team1(); } -void spawnfunc_team_CTF_bluespawn() { spawnfunc_info_player_team2(); } + +// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now void spawnfunc_item_flight() { spawnfunc_item_jetpack(); } diff --git a/qcsrc/server/t_teleporters.qc b/qcsrc/server/t_teleporters.qc index 1705d8f8ea..2cfdd8a772 100644 --- a/qcsrc/server/t_teleporters.qc +++ b/qcsrc/server/t_teleporters.qc @@ -75,6 +75,11 @@ void spawn_tdeath(vector v0, entity e, vector v) #define TELEPORT_FLAGS_WARPZONE 0 #define TELEPORT_FLAGS_PORTAL (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH) #define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH) + +// types for .teleportable entity setting +#define TELEPORT_NORMAL 1 // play sounds/effects etc +#define TELEPORT_SIMPLE 2 // only do teleport, nothing special + void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) { entity telefragger; @@ -87,7 +92,7 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle makevectors (to_angles); - if(player.classname == "player") // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers + if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers { if(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps { @@ -131,10 +136,12 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle { player.pusher = teleporter.owner; player.pushltime = time + autocvar_g_maxpushtime; + player.istypefrag = player.BUTTON_CHAT; } else { player.pushltime = 0; + player.istypefrag = 0; } player.lastteleporttime = time; @@ -195,20 +202,17 @@ void Teleport_Touch (void) if (self.active != ACTIVE_ACTIVE) return; - if not(other.iscreature) - return; - - // for gameplay: vehicles can't teleport - if (other.vehicle_flags & VHF_ISVEHICLE) + if not(other.teleportable) return; - if(other.vehicle) - return; - - if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET) - return; + if(other.vehicle) + if(!other.vehicle.teleportable) + return; + + if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET) + return; - if (other.deadflag != DEAD_NO) + if(other.deadflag != DEAD_NO) return; if(self.team) diff --git a/qcsrc/server/target_spawn.qc b/qcsrc/server/target_spawn.qc index e04e2cad88..ebb89aa5c9 100644 --- a/qcsrc/server/target_spawn.qc +++ b/qcsrc/server/target_spawn.qc @@ -22,7 +22,7 @@ void target_spawn_helper_setsize() setsize(self, self.mins, self.maxs); } -void target_spawn_useon(entity e) +void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act) { float i, n, valuefieldpos; string key, value, valuefield, valueoffset, valueoffsetrandom; @@ -30,15 +30,8 @@ void target_spawn_useon(entity e) vector data, data2; entity oldself; entity oldactivator; - entity kt, t2, t3, t4; - - n = tokenize_console(self.message); - self.target_spawn_activator = activator; - kt = find(world, targetname, self.killtarget); - t2 = find(world, targetname, self.target2); - t3 = find(world, targetname, self.target3); - t4 = find(world, targetname, self.target4); + n = tokenize_console(msg); for(i = 0; i < n-1; i += 2) { @@ -101,7 +94,7 @@ void target_spawn_useon(entity e) } else if(value == "activator") { - valueent = activator; + valueent = act; value = ""; } else if(value == "other") @@ -111,8 +104,8 @@ void target_spawn_useon(entity e) } else if(value == "pusher") { - if(time < activator.pushltime) - valueent = activator.pusher; + if(time < act.pushltime) + valueent = act.pusher; else valueent = world; value = ""; @@ -221,7 +214,7 @@ void target_spawn_useon(entity e) oldactivator = activator; self = e; - activator = oldself.target_spawn_activator; + activator = act; self.target_spawn_spawnfunc(); @@ -237,6 +230,20 @@ void target_spawn_useon(entity e) } } +void target_spawn_useon(entity e) +{ + self.target_spawn_activator = activator; + target_spawn_edit_entity( + e, + self.message, + find(world, targetname, self.killtarget), + find(world, targetname, self.target2), + find(world, targetname, self.target3), + find(world, targetname, self.target4), + activator + ); +} + float target_spawn_cancreate() { float c; diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 960e59d674..b8f2f3ac87 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -6,22 +6,7 @@ float c1, c2, c3, c4; // # of bots on those teams float cb1, cb2, cb3, cb4; -float audit_teams_time; - -float IsTeamBalanceForced() -{ - if(intermission_running) - return 0; // no rebalancing whatsoever please - if(!teamplay) - return 0; - if(autocvar_g_campaign) - return 0; - if(autocvar_bot_vs_human && (c3==-1 && c4==-1)) - return 0; - if(!autocvar_g_balance_teams_force) - return -1; - return 1; -} +//float audit_teams_time; void TeamchangeFrags(entity e) { @@ -68,7 +53,6 @@ string TeamNoName(float t) } void dom_init(); -void ctf_init(); void runematch_init(); void tdm_init(); void entcs_init(); @@ -157,10 +141,9 @@ void InitGameplayMode() if(g_ctf) { ActivateTeamplay(); - g_ctf_ignore_frags = autocvar_g_ctf_ignore_frags; fraglimit_override = autocvar_capturelimit_override; leadlimit_override = autocvar_captureleadlimit_override; - ctf_init(); + MUTATOR_ADD(gamemode_ctf); have_team_spawns = -1; // request team spawns } @@ -228,6 +211,7 @@ void InitGameplayMode() { ActivateTeamplay(); have_team_spawns = -1; // request team spawns + MUTATOR_ADD(gamemode_onslaught); } if(g_race) @@ -462,6 +446,7 @@ void CheckAllowedTeams (entity for_whom) c1 = c2 = c3 = c4 = -1; cb1 = cb2 = cb3 = cb4 = 0; + teament_name = string_null; if(g_onslaught) { // onslaught is special @@ -561,8 +546,6 @@ void CheckAllowedTeams (entity for_whom) float PlayerValue(entity p) { - if(IsTeamBalanceForced() == 1) - return 1; return 1; // FIXME: it always returns 1... } @@ -642,11 +625,73 @@ void GetTeamCounts(entity ignore) } } +float TeamSmallerEqThanTeam(float ta, float tb, entity e) +{ + // we assume that CheckAllowedTeams and GetTeamCounts have already been called + float f; + float ca = -1, cb = -1, cba = 0, cbb = 0, sa = 0, sb = 0; + + switch(ta) + { + case 1: ca = c1; cba = cb1; sa = team1_score; break; + case 2: ca = c2; cba = cb2; sa = team2_score; break; + case 3: ca = c3; cba = cb3; sa = team3_score; break; + case 4: ca = c4; cba = cb4; sa = team4_score; break; + } + switch(tb) + { + case 1: cb = c1; cbb = cb1; sb = team1_score; break; + case 2: cb = c2; cbb = cb2; sb = team2_score; break; + case 3: cb = c3; cbb = cb3; sb = team3_score; break; + case 4: cb = c4; cbb = cb4; sb = team4_score; break; + } + + // invalid + if(ca < 0 || cb < 0) + return FALSE; + + // equal + if(ta == tb) + return TRUE; + + if(clienttype(e) == CLIENTTYPE_REAL) + { + if(bots_would_leave) + { + ca -= cba * 0.999; + cb -= cbb * 0.999; + } + } + + // keep teams alive (teams of size 0 always count as smaller, ignoring score) + if(ca < 1) + if(cb >= 1) + return TRUE; + if(ca >= 1) + if(cb < 1) + return FALSE; + + // first, normalize + f = max(ca, cb, 1); + ca /= f; + cb /= f; + f = max(sa, sb, 1); + sa /= f; + sb /= f; + + // the more we're at the end of the match, the more take scores into account + f = bound(0, game_completion_ratio * autocvar_g_balance_teams_scorefactor, 1); + ca += (sa - ca) * f; + cb += (sb - cb) * f; + + return ca <= cb; +} + // returns # of smallest team (1, 2, 3, 4) // NOTE: Assumes CheckAllowedTeams has already been called! float FindSmallestTeam(entity pl, float ignore_pl) { - float totalteams, balance_type, maxc; + float totalteams, t; totalteams = 0; // find out what teams are available @@ -687,49 +732,26 @@ float FindSmallestTeam(entity pl, float ignore_pl) else GetTeamCounts(world); - // c1...c4 now have counts of each team - // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker - - // 2 gives priority to what team you're already on, 1 goes in order - // 2 doesn't seem to work though... - balance_type = 1; - - if(bots_would_leave) - //if(pl.classname != "player") - if(clienttype(pl) != CLIENTTYPE_BOT) - { - c1 -= cb1 * 255.0/256.0; - c2 -= cb2 * 255.0/256.0; - c3 -= cb3 * 255.0/256.0; - c4 -= cb4 * 255.0/256.0; - } - maxc = max4(c1, c2, c3, c4); - RandomSelection_Init(); - if(balance_type == 1) - { - // 1: use team count, then score (note: can only use 8 significant bits of score) - if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + float2range01(-team1_score) / 256.0); - if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c2) + float2range01(-team2_score) / 256.0); - if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c3) + float2range01(-team3_score) / 256.0); - if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c4) + float2range01(-team4_score) / 256.0); - } - else if(balance_type == 2) - { - // 1: use team count, if equal prefer own team - if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM1) / 512.0); - if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM2) / 512.0); - if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM3) / 512.0); - if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM4) / 512.0); - } - else if(balance_type == 3) - { - // 1: use team count, then score, if equal prefer own team (probably fails due to float accuracy problems) - if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + float2range01(-team1_score + 0.5 * (self.team == COLOR_TEAM1)) / 256.0); - if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c2) + float2range01(-team2_score + 0.5 * (self.team == COLOR_TEAM2)) / 256.0); - if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c3) + float2range01(-team3_score + 0.5 * (self.team == COLOR_TEAM3)) / 256.0); - if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c4) + float2range01(-team4_score + 0.5 * (self.team == COLOR_TEAM4)) / 256.0); - } + + t = 1; + if(TeamSmallerEqThanTeam(2, t, pl)) + t = 2; + if(TeamSmallerEqThanTeam(3, t, pl)) + t = 3; + if(TeamSmallerEqThanTeam(4, t, pl)) + t = 4; + + // now t is the minimum, or A minimum! + if(t == 1 || TeamSmallerEqThanTeam(1, t, pl)) + RandomSelection_Add(world, 1, string_null, 1, 1); + if(t == 2 || TeamSmallerEqThanTeam(2, t, pl)) + RandomSelection_Add(world, 2, string_null, 1, 1); + if(t == 3 || TeamSmallerEqThanTeam(3, t, pl)) + RandomSelection_Add(world, 3, string_null, 1, 1); + if(t == 4 || TeamSmallerEqThanTeam(4, t, pl)) + RandomSelection_Add(world, 4, string_null, 1, 1); + return RandomSelection_chosen_float; } @@ -812,7 +834,7 @@ float JoinBestTeam(entity pl, float only_return_best, float forcebestteam) //void() ctf_playerchanged; void SV_ChangeTeam(float _color) { - float scolor, dcolor, steam, dteam, dbotcount, scount, dcount; + float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount; // in normal deathmatch we can just apply the color and we're done if(!teamplay) { @@ -860,62 +882,15 @@ void SV_ChangeTeam(float _color) return; // changing teams is not allowed } - if(autocvar_g_balance_teams_prevent_imbalance) + // autocvar_g_balance_teams_prevent_imbalance only makes sense if autocvar_g_balance_teams is on, as it makes the team selection dialog pointless + if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance) { - // only allow changing to a smaller or equal size team - - // find out what teams are available - //CheckAllowedTeams(); - // count how many players on each team - GetTeamCounts(world); - - // get desired team - if(dteam == 1 && c1 >= 0)//dcolor == COLOR_TEAM1 - 1) + GetTeamCounts(self); + if(!TeamSmallerEqThanTeam(dteam, steam, self)) { - dcount = c1; - dbotcount = cb1; - } - else if(dteam == 2 && c2 >= 0)//dcolor == COLOR_TEAM2 - 1) - { - 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"); @@ -934,7 +909,6 @@ void SV_ChangeTeam(float _color) if(self.deadflag == DEAD_NO) Damage(self, self, self, 100000, DEATH_TEAMCHANGE, self.origin, '0 0 0'); } - //ctf_playerchanged(); } void ShufflePlayerOutOfTeam (float source_team) @@ -979,7 +953,7 @@ void ShufflePlayerOutOfTeam (float source_team) steam = COLOR_TEAM2; else if(source_team == 3) steam = COLOR_TEAM3; - else if(source_team == 4) + else // if(source_team == 4) steam = COLOR_TEAM4; lowest_bot = world; @@ -1078,87 +1052,6 @@ void ShufflePlayerOutOfTeam (float source_team) centerprint(selected, strcat("You have been moved into a different team to improve team balance\nYou are now on: ", ColoredTeamName(selected.team))); } -void CauseRebalance(float source_team, float howmany_toomany) -{ - if(IsTeamBalanceForced() == 1) - { - bprint("Rebalancing Teams\n"); - ShufflePlayerOutOfTeam(source_team); - } -} - -// part of g_balance_teams_force -// occasionally perform an audit of the teams to make -// sure they're more or less balanced in player count. -void AuditTeams() -{ - float numplayers, numteams, smallest, toomany; - float balance; - balance = IsTeamBalanceForced(); - if(balance == 0) - return; - - if(audit_teams_time > time) - return; - - audit_teams_time = time + 4 + random(); - -// bprint("Auditing teams\n"); - - CheckAllowedTeams(world); - GetTeamCounts(world); - - - numteams = numplayers = smallest = 0; - if(c1 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c1; - smallest = c1; - } - if(c2 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c2; - if(c2 < smallest) - smallest = c2; - } - if(c3 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c3; - if(c3 < smallest) - smallest = c3; - } - if(c4 >= 0) - { - numteams = numteams + 1; - numplayers = numplayers + c4; - if(c4 < smallest) - smallest = c4; - } - - if(numplayers <= 0) - return; // no players to move around - if(numteams < 2) - return; // don't bother shuffling if for some reason there aren't any teams - - toomany = smallest + 1; - - if(c1 && c1 > toomany) - CauseRebalance(1, c1 - toomany); - if(c2 && c2 > toomany) - CauseRebalance(2, c2 - toomany); - if(c3 && c3 > toomany) - CauseRebalance(3, c3 - toomany); - if(c4 && c4 > toomany) - CauseRebalance(4, c4 - toomany); - - // if teams are still unbalanced, balance them further in the next audit, - // which will happen sooner (keep doing rapid audits until things are in order) - audit_teams_time = time + 0.7 + random()*0.3; -} - // code from here on is just to support maps that don't have team entities void tdm_spawnteam (string teamname, float teamcolor) { diff --git a/qcsrc/server/tturrets/system/system_main.qc b/qcsrc/server/tturrets/system/system_main.qc index 7247a3a347..c8bbf537c3 100644 --- a/qcsrc/server/tturrets/system/system_main.qc +++ b/qcsrc/server/tturrets/system/system_main.qc @@ -726,7 +726,7 @@ entity turret_select_target() m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias; } else - self.enemy = world; + e_enemy = self.enemy = world; e = findradius(self.origin, self.target_range); diff --git a/qcsrc/server/tturrets/system/system_misc.qc b/qcsrc/server/tturrets/system/system_misc.qc index 1c0a3bad52..5a94b44234 100644 --- a/qcsrc/server/tturrets/system/system_misc.qc +++ b/qcsrc/server/tturrets/system/system_misc.qc @@ -60,7 +60,7 @@ vector shortangle_vxy(vector ang1, vector ang2) vector real_origin(entity ent) { entity e; - vector v; + vector v = ((ent.absmin + ent.absmax) * 0.5); e = ent.tag_entity; while(e) @@ -68,7 +68,7 @@ vector real_origin(entity ent) v = v + ((e.absmin + e.absmax) * 0.5); e = e.tag_entity; } - v = v + ((ent.absmin + ent.absmax) * 0.5); + return v; } diff --git a/qcsrc/server/tturrets/units/unit_ewheel.qc b/qcsrc/server/tturrets/units/unit_ewheel.qc index 6b8f8e7d4c..31b984e391 100644 --- a/qcsrc/server/tturrets/units/unit_ewheel.qc +++ b/qcsrc/server/tturrets/units/unit_ewheel.qc @@ -7,12 +7,15 @@ void ewheel_attack() { float i; - + entity _mis; + for (i = 0; i < 1; ++i) { turret_do_updates(self); - turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_LASER, TRUE, TRUE); + _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_LASER, TRUE, TRUE); + _mis.missile_flags = MIF_SPLASH; + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); self.tur_head.frame += 2; @@ -252,6 +255,7 @@ void turret_ewheel_dinit() self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; self.iscreature = TRUE; + self.teleportable = TELEPORT_NORMAL; self.damagedbycontents = TRUE; self.movetype = MOVETYPE_WALK; self.solid = SOLID_SLIDEBOX; diff --git a/qcsrc/server/tturrets/units/unit_flac.qc b/qcsrc/server/tturrets/units/unit_flac.qc index 975abbd4c9..7c21ba7abe 100644 --- a/qcsrc/server/tturrets/units/unit_flac.qc +++ b/qcsrc/server/tturrets/units/unit_flac.qc @@ -29,7 +29,8 @@ void turret_flac_attack() pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); proj.think = turret_flac_projectile_think_explode; proj.nextthink = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01); - + proj.missile_flags = MIF_SPLASH | MIF_PROXY; + self.tur_head.frame = self.tur_head.frame + 1; if (self.tur_head.frame >= 4) self.tur_head.frame = 0; diff --git a/qcsrc/server/tturrets/units/unit_hellion.qc b/qcsrc/server/tturrets/units/unit_hellion.qc index 08bfad2a61..75360bcd71 100644 --- a/qcsrc/server/tturrets/units/unit_hellion.qc +++ b/qcsrc/server/tturrets/units/unit_hellion.qc @@ -79,6 +79,7 @@ void turret_hellion_attack() missile.flags = FL_PROJECTILE; missile.tur_health = time + 9; missile.tur_aimpos = randomvec() * 128; + missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; self.tur_head.frame += 1; } diff --git a/qcsrc/server/tturrets/units/unit_hk.qc b/qcsrc/server/tturrets/units/unit_hk.qc index d61289dcbe..8676091f58 100644 --- a/qcsrc/server/tturrets/units/unit_hk.qc +++ b/qcsrc/server/tturrets/units/unit_hk.qc @@ -108,6 +108,8 @@ void turret_hk_missile_think() } else { + edist = 0; + ve = '0 0 0'; fe = 0; } @@ -255,7 +257,8 @@ void turret_hk_attack() missile.angles = vectoangles(missile.velocity); missile.cnt = time + 30; missile.ticrate = max(autocvar_sys_ticrate, 0.05); - + missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_AI; + if (self.tur_head.frame == 0) self.tur_head.frame = self.tur_head.frame + 1; diff --git a/qcsrc/server/tturrets/units/unit_mlrs.qc b/qcsrc/server/tturrets/units/unit_mlrs.qc index 65c0ed0585..a12d9d7259 100644 --- a/qcsrc/server/tturrets/units/unit_mlrs.qc +++ b/qcsrc/server/tturrets/units/unit_mlrs.qc @@ -20,6 +20,7 @@ void turret_mlrs_attack() turret_tag_fire_update(); missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, TRUE, TRUE); missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed); + missile.missile_flags = MIF_SPLASH; te_explosion (missile.origin); } diff --git a/qcsrc/server/tturrets/units/unit_plasma.qc b/qcsrc/server/tturrets/units/unit_plasma.qc index d5abf654be..aacca522ec 100644 --- a/qcsrc/server/tturrets/units/unit_plasma.qc +++ b/qcsrc/server/tturrets/units/unit_plasma.qc @@ -27,7 +27,9 @@ void turret_plasma_dual_postthink() void turret_plasma_attack() { - turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); + entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); + missile.missile_flags = MIF_SPLASH; + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); if (self.tur_head.frame == 0) self.tur_head.frame = 1; @@ -35,7 +37,8 @@ void turret_plasma_attack() void turret_plasma_dual_attack() { - turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); + entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); + missile.missile_flags = MIF_SPLASH; pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); self.tur_head.frame += 1; } diff --git a/qcsrc/server/tturrets/units/unit_tessla.qc b/qcsrc/server/tturrets/units/unit_tessla.qc index 6c2cce6a73..85fb8bb182 100644 --- a/qcsrc/server/tturrets/units/unit_tessla.qc +++ b/qcsrc/server/tturrets/units/unit_tessla.qc @@ -5,7 +5,7 @@ void turret_tesla_fire(); entity toast(entity from, float range, float damage) { entity e; - entity etarget; + entity etarget = world; float d,dd; float r; diff --git a/qcsrc/server/tturrets/units/unit_walker.qc b/qcsrc/server/tturrets/units/unit_walker.qc index 646fccccb2..599eb57769 100644 --- a/qcsrc/server/tturrets/units/unit_walker.qc +++ b/qcsrc/server/tturrets/units/unit_walker.qc @@ -199,7 +199,7 @@ void walker_fire_rocket(vector org) rocket.tur_shotorg = randomvec() * 512; rocket.cnt = time + 1; rocket.enemy = self.enemy; - + if (random() < 0.01) rocket.think = walker_rocket_loop; else @@ -215,7 +215,8 @@ void walker_fire_rocket(vector org) rocket.flags = FL_PROJECTILE; rocket.solid = SOLID_BBOX; rocket.tur_health = time + 9; - + rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; + CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound } @@ -592,6 +593,7 @@ void turret_walker_dinit() self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; self.iscreature = TRUE; + self.teleportable = TELEPORT_NORMAL; self.damagedbycontents = TRUE; self.movetype = MOVETYPE_WALK; self.solid = SOLID_SLIDEBOX; diff --git a/qcsrc/server/vehicles/bumblebee.qc b/qcsrc/server/vehicles/bumblebee.qc new file mode 100644 index 0000000000..4b0f1d13a4 --- /dev/null +++ b/qcsrc/server/vehicles/bumblebee.qc @@ -0,0 +1,1092 @@ +#define BRG_SETUP 2 +#define BRG_START 4 +#define BRG_END 8 + +#ifdef SVQC +// Auto cvars +float autocvar_g_vehicle_bumblebee_speed_forward; +float autocvar_g_vehicle_bumblebee_speed_strafe; +float autocvar_g_vehicle_bumblebee_speed_up; +float autocvar_g_vehicle_bumblebee_speed_down; +float autocvar_g_vehicle_bumblebee_turnspeed; +float autocvar_g_vehicle_bumblebee_pitchspeed; +float autocvar_g_vehicle_bumblebee_pitchlimit; +float autocvar_g_vehicle_bumblebee_friction; + +float autocvar_g_vehicle_bumblebee_energy; +float autocvar_g_vehicle_bumblebee_energy_regen; +float autocvar_g_vehicle_bumblebee_energy_regen_pause; + +float autocvar_g_vehicle_bumblebee_health; +float autocvar_g_vehicle_bumblebee_health_regen; +float autocvar_g_vehicle_bumblebee_health_regen_pause; + +float autocvar_g_vehicle_bumblebee_shield; +float autocvar_g_vehicle_bumblebee_shield_regen; +float autocvar_g_vehicle_bumblebee_shield_regen_pause; + +float autocvar_g_vehicle_bumblebee_cannon_cost; +float autocvar_g_vehicle_bumblebee_cannon_damage; +float autocvar_g_vehicle_bumblebee_cannon_radius; +float autocvar_g_vehicle_bumblebee_cannon_refire; +float autocvar_g_vehicle_bumblebee_cannon_speed; +float autocvar_g_vehicle_bumblebee_cannon_spread; +float autocvar_g_vehicle_bumblebee_cannon_force; + +float autocvar_g_vehicle_bumblebee_cannon_ammo; +float autocvar_g_vehicle_bumblebee_cannon_ammo_regen; +float autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause; + +var float autocvar_g_vehicle_bumblebee_cannon_lock = 0; + +float autocvar_g_vehicle_bumblebee_cannon_turnspeed; +float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down; +float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up; +float autocvar_g_vehicle_bumblebee_cannon_turnlimit_in; +float autocvar_g_vehicle_bumblebee_cannon_turnlimit_out; + + +float autocvar_g_vehicle_bumblebee_raygun_turnspeed; +float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down; +float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up; +float autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides; + +float autocvar_g_vehicle_bumblebee_raygun_range; +float autocvar_g_vehicle_bumblebee_raygun_dps; +float autocvar_g_vehicle_bumblebee_raygun_aps; +float autocvar_g_vehicle_bumblebee_raygun_fps; + +float autocvar_g_vehicle_bumblebee_raygun; +float autocvar_g_vehicle_bumblebee_healgun_hps; +float autocvar_g_vehicle_bumblebee_healgun_hmax; +float autocvar_g_vehicle_bumblebee_healgun_aps; +float autocvar_g_vehicle_bumblebee_healgun_amax; +float autocvar_g_vehicle_bumblebee_healgun_sps; +float autocvar_g_vehicle_bumblebee_healgun_locktime; + +float autocvar_g_vehicle_bumblebee_respawntime; + +float autocvar_g_vehicle_bumblebee_blowup_radius; +float autocvar_g_vehicle_bumblebee_blowup_coredamage; +float autocvar_g_vehicle_bumblebee_blowup_edgedamage; +float autocvar_g_vehicle_bumblebee_blowup_forceintensity; +var vector autocvar_g_vehicle_bumblebee_bouncepain; + +var float autocvar_g_vehicle_bumblebee = 0; + + +float bumble_raygun_send(entity to, float sf); + +#define BUMB_MIN '-130 -130 -130' +#define BUMB_MAX '130 130 130' + +void bumb_fire_cannon(entity _gun, string _tagname, entity _owner) +{ + vector v = gettaginfo(_gun, gettagindex(_gun, _tagname)); + vehicles_projectile("bigplasma_muzzleflash", "weapons/flacexp3.wav", + v, normalize(v_forward + randomvec() * autocvar_g_vehicle_bumblebee_cannon_spread) * autocvar_g_vehicle_bumblebee_cannon_speed, + autocvar_g_vehicle_bumblebee_cannon_damage, autocvar_g_vehicle_bumblebee_cannon_radius, autocvar_g_vehicle_bumblebee_cannon_force, 0, + DEATH_BUMB_GUN, PROJECTILE_BUMBLE_GUN, 0, TRUE, TRUE, _owner); +} + +float bumb_gunner_frame() +{ + entity vehic = self.vehicle.owner; + entity gun = self.vehicle; + entity gunner = self; + self = vehic; + + + + + vehic.solid = SOLID_NOT; + //setorigin(gunner, vehic.origin); + gunner.velocity = vehic.velocity; + + float _in, _out; + vehic.angles_x *= -1; + makevectors(vehic.angles); + vehic.angles_x *= -1; + if((gun == vehic.gun1)) + { + _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in; + _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out; + setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * 128); + } + else + { + _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out; + _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in; + setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * -128); + } + + crosshair_trace(gunner); + vector _ct = trace_endpos; + vector ad; + + if(autocvar_g_vehicle_bumblebee_cannon_lock) + { + if(gun.lock_time < time) + gun.enemy = world; + + if(trace_ent) + if(trace_ent.movetype) + if(trace_ent.takedamage) + if(!trace_ent.deadflag) + { + if(teamplay) + { + if(trace_ent.team != gunner.team) + { + gun.enemy = trace_ent; + gun.lock_time = time + 5; + } + } + else + { + gun.enemy = trace_ent; + gun.lock_time = time + 5; + } + } + } + + if(gun.enemy) + { + float i, distance, impact_time; + + vector vf = real_origin(gun.enemy); + vector _vel = gun.enemy.velocity; + if(gun.enemy.movetype == MOVETYPE_WALK) + _vel_z *= 0.1; + + + ad = vf; + for(i = 0; i < 4; ++i) + { + distance = vlen(ad - gunner.origin); + impact_time = distance / autocvar_g_vehicle_bumblebee_cannon_speed; + ad = vf + _vel * impact_time; + } + trace_endpos = ad; + + + UpdateAuxiliaryXhair(gunner, ad, '1 0 1', 1); + vehicle_aimturret(vehic, trace_endpos, gun, "fire", + autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up, + _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed); + + } + else + vehicle_aimturret(vehic, _ct, gun, "fire", + autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up, + _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed); + + if(gunner.BUTTON_ATCK) + if(time > gun.attack_finished_single) + if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost) + { + gun.vehicle_energy -= autocvar_g_vehicle_bumblebee_cannon_cost; + bumb_fire_cannon(gun, "fire", gunner); + gun.delay = time; + gun.attack_finished_single = time + autocvar_g_vehicle_bumblebee_cannon_refire; + } + + VEHICLE_UPDATE_PLAYER(gunner, health, bumblebee); + + if(vehic.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(gunner, shield, bumblebee); + + ad = gettaginfo(gun, gettagindex(gun, "fire")); + traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, gun); + + UpdateAuxiliaryXhair(gunner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), 0); + + if(vehic.owner) + UpdateAuxiliaryXhair(vehic.owner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), ((gunner == vehic.gunner1) ? 1 : 2)); + + vehic.solid = SOLID_BBOX; + gunner.BUTTON_ATCK = gunner.BUTTON_ATCK2 = gunner.BUTTON_CROUCH = 0; + gunner.vehicle_energy = (gun.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; + + self = gunner; + return 1; +} + +void bumb_gunner_exit(float _exitflag) +{ + + + if(clienttype(self) == CLIENTTYPE_REAL) + { + msg_entity = self; + WriteByte(MSG_ONE, SVC_SETVIEWPORT); + WriteEntity(MSG_ONE, self); + + WriteByte(MSG_ONE, SVC_SETVIEWANGLES); + WriteAngle(MSG_ONE, 0); + WriteAngle(MSG_ONE, self.vehicle.angles_y); + WriteAngle(MSG_ONE, 0); + } + + CSQCVehicleSetup(self, HUD_NORMAL); + setsize(self, PL_MIN, PL_MAX); + + self.takedamage = DAMAGE_AIM; + self.solid = SOLID_SLIDEBOX; + self.movetype = MOVETYPE_WALK; + self.effects &~= EF_NODRAW; + self.alpha = 1; + self.PlayerPhysplug = SUB_Null; + self.view_ofs = PL_VIEW_OFS; + self.event_damage = PlayerDamage; + self.hud = HUD_NORMAL; + self.switchweapon = self.vehicle.switchweapon; + + vh_player = self; + vh_vehicle = self.vehicle; + MUTATOR_CALLHOOK(VehicleExit); + self = vh_player; + self.vehicle = vh_vehicle; + + self.vehicle.vehicle_hudmodel.viewmodelforclient = self.vehicle; + + fixedmakevectors(self.vehicle.owner.angles); + + if(self == self.vehicle.owner.gunner1) + { + self.vehicle.owner.gunner1 = world; + } + else if(self == self.vehicle.owner.gunner2) + { + self.vehicle.owner.gunner2 = world; + v_right *= -1; + } + else + dprint("^1self != gunner1 or gunner2, this is a BIG PROBLEM, tell tZork this happend.\n"); + + vector spot = self.vehicle.owner.origin + + v_up * 128 + v_right * 300; + spot = vehicles_findgoodexit(spot); + //setorigin(self , spot); + + self.velocity = 0.75 * self.vehicle.owner.velocity + normalize(spot - self.vehicle.owner.origin) * 200; + self.velocity_z += 10; + + self.vehicle.phase = time + 5; + self.vehicle = world; +} + +float bumb_gunner_enter() +{ + RemoveGrapplingHook(other); + entity _gun, _gunner; + if(!self.gunner1) + { + _gun = self.gun1; + _gunner = self.gunner1; + self.gunner1 = other; + } + else if(!self.gunner2) + { + _gun = self.gun2; + _gunner = self.gunner2; + self.gunner2 = other; + } + else + { + dprint("^1ERROR:^7Tried to enter a fully occupied vehicle!\n"); + return FALSE; + } + + _gunner = other; + _gunner.vehicle = _gun; + _gun.switchweapon = other.switchweapon; + _gun.vehicle_exit = bumb_gunner_exit; + + other.angles = self.angles; + other.takedamage = DAMAGE_NO; + other.solid = SOLID_NOT; + other.movetype = MOVETYPE_NOCLIP; + other.alpha = -1; + other.event_damage = SUB_Null; + other.view_ofs = '0 0 0'; + other.hud = _gun.hud; + other.PlayerPhysplug = _gun.PlayerPhysplug; + other.vehicle_ammo1 = self.vehicle_ammo1; + other.vehicle_ammo2 = self.vehicle_ammo2; + other.vehicle_reload1 = self.vehicle_reload1; + other.vehicle_reload2 = self.vehicle_reload2; + other.vehicle_energy = self.vehicle_energy; + other.PlayerPhysplug = bumb_gunner_frame; + other.flags &~= FL_ONGROUND; + + msg_entity = other; + WriteByte(MSG_ONE, SVC_SETVIEWPORT); + WriteEntity(MSG_ONE, _gun.vehicle_viewport); + WriteByte(MSG_ONE, SVC_SETVIEWANGLES); + WriteAngle(MSG_ONE, _gun.angles_x + self.angles_x); // tilt + WriteAngle(MSG_ONE, _gun.angles_y + self.angles_y); // yaw + WriteAngle(MSG_ONE, 0); // roll + _gun.vehicle_hudmodel.viewmodelforclient = other; + + CSQCVehicleSetup(other, other.hud); + + vh_player = other; + vh_vehicle = _gun; + MUTATOR_CALLHOOK(VehicleEnter); + other = vh_player; + _gun = vh_vehicle; + + return TRUE; +} + +float vehicles_valid_pilot() +{ + if(other.classname != "player") + return FALSE; + + if(other.deadflag != DEAD_NO) + return FALSE; + + if(other.vehicle != world) + return FALSE; + + if(clienttype(other) != CLIENTTYPE_REAL) + if(!autocvar_g_vehicles_allow_bots) + return FALSE; + + if(teamplay && other.team != self.team) + return FALSE; + + return TRUE; +} + +void bumb_touch() +{ + + if(self.gunner1 != world && self.gunner2 != world) + { + vehicles_touch(); + return; + } + + if(vehicles_valid_pilot()) + { + if(self.gun1.phase <= time) + if(bumb_gunner_enter()) + return; + + if(self.gun2.phase <= time) + if(bumb_gunner_enter()) + return; + } + + vehicles_touch(); +} + +void bumb_regen() +{ + if(self.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time) + self.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo, + self.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime); + + if(self.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time) + self.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo, + self.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime); + + if(self.vehicle_flags & VHF_SHIELDREGEN) + vehicles_regen(self.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, TRUE); + + if(self.vehicle_flags & VHF_HEALTHREGEN) + vehicles_regen(self.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, FALSE); + + if(self.vehicle_flags & VHF_ENERGYREGEN) + vehicles_regen(self.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, FALSE); + +} + +float bumb_pilot_frame() +{ + entity pilot, vehic; + vector newvel; + + pilot = self; + vehic = self.vehicle; + self = vehic; + + + if(vehic.deadflag != DEAD_NO) + { + self = pilot; + pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = 0; + return 1; + } + + bumb_regen(); + + crosshair_trace(pilot); + + vector vang; + float ftmp; + + vang = vehic.angles; + newvel = vectoangles(normalize(trace_endpos - self.origin + '0 0 32')); + vang_x *= -1; + newvel_x *= -1; + if(newvel_x > 180) newvel_x -= 360; + if(newvel_x < -180) newvel_x += 360; + if(newvel_y > 180) newvel_y -= 360; + if(newvel_y < -180) newvel_y += 360; + + ftmp = shortangle_f(pilot.v_angle_y - vang_y, vang_y); + if(ftmp > 180) ftmp -= 360; + if(ftmp < -180) ftmp += 360; + vehic.avelocity_y = bound(-autocvar_g_vehicle_bumblebee_turnspeed, ftmp + vehic.avelocity_y * 0.9, autocvar_g_vehicle_bumblebee_turnspeed); + + // Pitch + ftmp = 0; + if(pilot.movement_x > 0 && vang_x < autocvar_g_vehicle_bumblebee_pitchlimit) + ftmp = 4; + else if(pilot.movement_x < 0 && vang_x > -autocvar_g_vehicle_bumblebee_pitchlimit) + ftmp = -8; + + newvel_x = bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel_x , autocvar_g_vehicle_bumblebee_pitchlimit); + ftmp = vang_x - bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel_x + ftmp, autocvar_g_vehicle_bumblebee_pitchlimit); + vehic.avelocity_x = bound(-autocvar_g_vehicle_bumblebee_pitchspeed, ftmp + vehic.avelocity_x * 0.9, autocvar_g_vehicle_bumblebee_pitchspeed); + + vehic.angles_x = anglemods(vehic.angles_x); + vehic.angles_y = anglemods(vehic.angles_y); + vehic.angles_z = anglemods(vehic.angles_z); + + makevectors('0 1 0' * vehic.angles_y); + newvel = vehic.velocity * -autocvar_g_vehicle_bumblebee_friction; + + if(pilot.movement_x != 0) + { + if(pilot.movement_x > 0) + newvel += v_forward * autocvar_g_vehicle_bumblebee_speed_forward; + else if(pilot.movement_x < 0) + newvel -= v_forward * autocvar_g_vehicle_bumblebee_speed_forward; + } + + if(pilot.movement_y != 0) + { + if(pilot.movement_y < 0) + newvel -= v_right * autocvar_g_vehicle_bumblebee_speed_strafe; + else if(pilot.movement_y > 0) + newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe; + ftmp = newvel * v_right; + ftmp *= frametime * 0.1; + vehic.angles_z = bound(-15, vehic.angles_z + ftmp, 15); + } + else + { + vehic.angles_z *= 0.95; + if(vehic.angles_z >= -1 && vehic.angles_z <= -1) + vehic.angles_z = 0; + } + + if(pilot.BUTTON_CROUCH) + newvel -= v_up * autocvar_g_vehicle_bumblebee_speed_down; + else if(pilot.BUTTON_JUMP) + newvel += v_up * autocvar_g_vehicle_bumblebee_speed_up; + + vehic.velocity += newvel * frametime; + pilot.velocity = pilot.movement = vehic.velocity; + + + if(autocvar_g_vehicle_bumblebee_healgun_locktime) + { + if(vehic.tur_head.lock_time < time || vehic.tur_head.enemy.deadflag) + vehic.tur_head.enemy = world; + + if(trace_ent) + if(trace_ent.movetype) + if(trace_ent.takedamage) + if(!trace_ent.deadflag) + { + if(teamplay) + { + if(trace_ent.team == pilot.team) + { + vehic.tur_head.enemy = trace_ent; + vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime; + } + } + else + { + vehic.tur_head.enemy = trace_ent; + vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime; + } + } + + if(vehic.tur_head.enemy) + { + trace_endpos = real_origin(vehic.tur_head.enemy); + UpdateAuxiliaryXhair(pilot, trace_endpos, '0 0.75 0', 0); + } + } + + vang = vehicle_aimturret(vehic, trace_endpos, self.gun3, "fire", + autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up, + autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1, autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides, autocvar_g_vehicle_bumblebee_raygun_turnspeed); + + if((pilot.BUTTON_ATCK || pilot.BUTTON_ATCK2) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0)) + { + vehic.gun3.enemy.realowner = pilot; + vehic.gun3.enemy.effects &~= EF_NODRAW; + + vehic.gun3.enemy.hook_start = gettaginfo(vehic.gun3, gettagindex(vehic.gun3, "fire")); + vehic.gun3.enemy.SendFlags |= BRG_START; + + traceline(vehic.gun3.enemy.hook_start, vehic.gun3.enemy.hook_start + v_forward * autocvar_g_vehicle_bumblebee_raygun_range, MOVE_NORMAL, vehic); + + if(trace_ent) + { + if(autocvar_g_vehicle_bumblebee_raygun) + { + Damage(trace_ent, vehic, pilot, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime); + vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime; + } + else + { + if(trace_ent.deadflag == DEAD_NO) + if((teamplay && trace_ent.team == pilot.team) || !teamplay) + { + + if(trace_ent.vehicle_flags & VHF_ISVEHICLE) + { + if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.tur_health) + trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.tur_health); + + if(autocvar_g_vehicle_bumblebee_healgun_hps) + trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.tur_health); + } + else if(trace_ent.flags & FL_CLIENT) + { + if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps) + trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); + + if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps) + trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * frametime, autocvar_g_vehicle_bumblebee_healgun_amax); + + trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); + } + else if(trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET) + { + if(trace_ent.health <= trace_ent.tur_health && autocvar_g_vehicle_bumblebee_healgun_hps) + trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.tur_health); + //else ..hmmm what? ammo? + + trace_ent.SendFlags |= TNSF_STATUS; + } + } + } + } + + vehic.gun3.enemy.hook_end = trace_endpos; + setorigin(vehic.gun3.enemy, trace_endpos); + vehic.gun3.enemy.SendFlags |= BRG_END; + + vehic.wait = time + 1; + } + else + vehic.gun3.enemy.effects |= EF_NODRAW; + /*{ + if(vehic.gun3.enemy) + remove(vehic.gun3.enemy); + + vehic.gun3.enemy = world; + } + */ + + VEHICLE_UPDATE_PLAYER(pilot, health, bumblebee); + VEHICLE_UPDATE_PLAYER(pilot, energy, bumblebee); + + pilot.vehicle_ammo1 = (vehic.gun1.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; + pilot.vehicle_ammo2 = (vehic.gun2.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; + + if(vehic.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(pilot, shield, bumblebee); + + vehic.angles_x *= -1; + makevectors(vehic.angles); + vehic.angles_x *= -1; + setorigin(pilot, vehic.origin + v_up * 48 + v_forward * 160); + + pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = pilot.BUTTON_CROUCH = 0; + self = pilot; + + return 1; +} + +void bumb_think() +{ + self.movetype = MOVETYPE_TOSS; + + //self.velocity = self.velocity * 0.5; + self.angles_z *= 0.8; + self.angles_x *= 0.8; + + self.nextthink = time + 0.05; + + if(!self.owner) + { + entity oldself = self; + if(self.gunner1) + { + self = self.gunner1; + oldself.gun1.vehicle_exit(VHEF_EJECT); + entity oldother = other; + other = self; + self = oldself; + self.phase = 0; + self.touch(); + other = oldother; + return; + } + + if(self.gunner2) + { + self = self.gunner2; + oldself.gun2.vehicle_exit(VHEF_EJECT); + entity oldother = other; + other = self; + self = oldself; + self.phase = 0; + self.touch(); + other = oldother; + return; + } + } + +} + +void bumb_enter() +{ + self.touch = bumb_touch; + self.nextthink = 0; + self.movetype = MOVETYPE_BOUNCEMISSILE; + //setattachment(self.owner, self.vehicle_viewport, ""); +} + +void bumb_exit(float eject) +{ + self.touch = vehicles_touch; + self.think = bumb_think; + self.nextthink = time; + + if(!self.owner) + return; + + fixedmakevectors(self.angles); + vector spot; + if(vlen(self.velocity) > autocvar_g_vehicle_bumblebee_speed_forward * 0.5) + spot = self.origin + v_up * 128 + v_forward * 200; + else + spot = self.origin + v_up * 128 - v_forward * 200; + + spot = vehicles_findgoodexit(spot); + + + self.owner.velocity = 0.75 * self.vehicle.velocity + normalize(spot - self.vehicle.origin) * 200; + self.owner.velocity_z += 10; + setorigin(self.owner, spot); + + /*if(eject) + { + spot = self.origin + v_forward * 100 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + //setorigin(self.owner , spot); + self.owner.velocity = (v_up + v_forward * 0.25) * 250; + self.owner.oldvelocity = self.owner.velocity; + } + else + { + if(vlen(self.velocity) > autocvar_g_vehicle_bumblebee_speed_forward * 0.5) + { + if(vlen(self.velocity) > autocvar_sv_maxairspeed) + self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed; + else + self.owner.velocity = self.velocity + v_forward * 100; + + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 128 + '0 0 32'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin - v_forward * 300 + '0 0 32'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; + //setorigin(self.owner , spot); + } + */ + + antilag_clear(self.owner); + self.owner = world; +} + +void bumb_blowup() +{ + RadiusDamage(self, self.enemy, autocvar_g_vehicle_bumblebee_blowup_coredamage, + autocvar_g_vehicle_bumblebee_blowup_edgedamage, + autocvar_g_vehicle_bumblebee_blowup_radius, self, + autocvar_g_vehicle_bumblebee_blowup_forceintensity, + DEATH_WAKIBLOWUP, world); + + sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("explosion_large"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1); + + if(self.owner.deadflag == DEAD_DYING) + self.owner.deadflag = DEAD_DEAD; + + remove(self); +} + +void bumb_diethink() +{ + if(time >= self.wait) + self.think = bumb_blowup; + + if(random() < 0.1) + { + sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1); + } + + self.nextthink = time + 0.1; +} + +void bumb_die() +{ + entity oldself = self; + + // Hide beam + if(self.gun3.enemy || !wasfreed(self.gun3.enemy)) + self.gun3.enemy.effects |= EF_NODRAW; + + if(self.gunner1) + { + self = self.gunner1; + oldself.gun1.vehicle_exit(VHEF_EJECT); + self = oldself; + } + + if(self.gunner2) + { + self = self.gunner2; + oldself.gun2.vehicle_exit(VHEF_EJECT); + self = oldself; + } + + self.vehicle_exit(VHEF_EJECT); + + fixedmakevectors(self.angles); + vehicle_tossgib(self.gun1, self.velocity + v_right * 300 + v_up * 100 + randomvec() * 200, "cannon_right", rint(random()), rint(random()), 6, randomvec() * 200); + vehicle_tossgib(self.gun2, self.velocity + v_right * -300 + v_up * 100 + randomvec() * 200, "cannon_left", rint(random()), rint(random()), 6, randomvec() * 200); + vehicle_tossgib(self.gun3, self.velocity + v_forward * 300 + v_up * -100 + randomvec() * 200, "raygun", rint(random()), rint(random()), 6, randomvec() * 300); + + entity _body = vehicle_tossgib(self, self.velocity + randomvec() * 200, "", rint(random()), rint(random()), 6, randomvec() * 100); + + if(random() > 0.5) + _body.touch = bumb_blowup; + else + _body.touch = SUB_Null; + + _body.think = bumb_diethink; + _body.nextthink = time; + _body.wait = time + 2 + (random() * 8); + _body.owner = self; + _body.enemy = self.enemy; + + pointparticles(particleeffectnum("explosion_medium"), findbetterlocation(self.origin, 16), '0 0 0', 1); + + self.health = 0; + self.event_damage = SUB_Null; + self.solid = SOLID_CORPSE; + self.takedamage = DAMAGE_NO; + self.deadflag = DEAD_DYING; + self.movetype = MOVETYPE_NONE; + self.effects = EF_NODRAW; + self.colormod = '0 0 0'; + self.avelocity = '0 0 0'; + self.velocity = '0 0 0'; + self.touch = SUB_Null; + self.nextthink = 0; + + setorigin(self, self.pos1); + +} + +void bumb_impact() +{ + if(autocvar_g_vehicle_bumblebee_bouncepain_x) + vehilces_impact(autocvar_g_vehicle_bumblebee_bouncepain_x, + autocvar_g_vehicle_bumblebee_bouncepain_y, + autocvar_g_vehicle_bumblebee_bouncepain_z); +} + +void bumb_spawn(float _f) +{ + /* + float i; + for(i=1; gettaginfo(self.gun1, i), gettaginfo_name; ++i) + { + + dprint(" ------- ^1gettaginfo_name^2(",ftos(i),") ^3=", gettaginfo_name, "\n"); + } + */ + if(!self.gun1) + { + // for some reason, autosizing of the shiled entity refuses to work for this one so set it up in advance. + self.vehicle_shieldent = spawn(); + self.vehicle_shieldent.effects = EF_LOWPRECISION; + setmodel(self.vehicle_shieldent, "models/vhshield.md3"); + setattachment(self.vehicle_shieldent, self, ""); + setorigin(self.vehicle_shieldent, real_origin(self) - self.origin); + self.vehicle_shieldent.scale = 512 / vlen(self.maxs - self.mins); + self.vehicle_shieldent.think = shieldhit_think; + self.vehicle_shieldent.alpha = -1; + self.vehicle_shieldent.effects = EF_LOWPRECISION | EF_NODRAW; + + self.gun1 = spawn(); + self.gun2 = spawn(); + self.gun3 = spawn(); + + self.vehicle_flags |= VHF_MULTISLOT; + + self.gun1.owner = self; + self.gun2.owner = self; + self.gun3.owner = self; + + setmodel(self.gun1, "models/vehicles/bumblebee_plasma_right.dpm"); + setmodel(self.gun2, "models/vehicles/bumblebee_plasma_left.dpm"); + setmodel(self.gun3, "models/vehicles/bumblebee_ray.dpm"); + + setattachment(self.gun1, self, "cannon_right"); + setattachment(self.gun2, self, "cannon_left"); + + // Angled bones are no fun, messes up gun-aim; so work arround it. + self.gun3.pos1 = self.angles; + self.angles = '0 0 0'; + vector ofs = gettaginfo(self, gettagindex(self, "raygun")); + ofs -= self.origin; + setattachment(self.gun3, self, ""); + setorigin(self.gun3, ofs); + self.angles = self.gun3.pos1; + + vehicle_addplayerslot(self, self.gun1, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumb_gunner_frame, bumb_gunner_exit); + vehicle_addplayerslot(self, self.gun2, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumb_gunner_frame, bumb_gunner_exit); + + setorigin(self.vehicle_hudmodel, '50 0 -5'); // Move cockpit forward - down. + setorigin(self.vehicle_viewport, '5 0 2'); // Move camera forward up + + //fixme-model-bones + setorigin(self.gun1.vehicle_hudmodel, '90 -27 -23'); + setorigin(self.gun1.vehicle_viewport, '-85 0 50'); + //fixme-model-bones + setorigin(self.gun2.vehicle_hudmodel, '90 27 -23'); + setorigin(self.gun2.vehicle_viewport, '-85 0 50'); + + self.scale = 1.5; + + // Raygun beam + if(self.gun3.enemy == world) + { + self.gun3.enemy = spawn(); + Net_LinkEntity(self.gun3.enemy, TRUE, 0, bumble_raygun_send); + self.gun3.enemy.SendFlags = BRG_SETUP; + self.gun3.enemy.cnt = autocvar_g_vehicle_bumblebee_raygun; + self.gun3.enemy.effects = EF_NODRAW | EF_LOWPRECISION; + } + } + + self.vehicle_health = autocvar_g_vehicle_bumblebee_health; + self.vehicle_shield = autocvar_g_vehicle_bumblebee_shield; + self.solid = SOLID_BBOX; + //self.movetype = MOVETYPE_BOUNCEMISSILE; + self.movetype = MOVETYPE_TOSS; + self.vehicle_impact = bumb_impact; + self.damageforcescale = 0.025; + + setorigin(self, self.origin + '0 0 25'); +} + +void spawnfunc_vehicle_bumblebee() +{ + if(!autocvar_g_vehicle_bumblebee) + { + remove(self); + return; + } + + precache_model("models/vehicles/bumblebee_body.dpm"); + precache_model("models/vehicles/bumblebee_plasma_left.dpm"); + precache_model("models/vehicles/bumblebee_plasma_right.dpm"); + precache_model("models/vehicles/bumblebee_ray.dpm"); + precache_model("models/vehicles/wakizashi_cockpit.dpm"); + precache_model("models/vehicles/spiderbot_cockpit.dpm"); + precache_model("models/vehicles/raptor_cockpit.dpm"); + + if(autocvar_g_vehicle_bumblebee_energy) + if(autocvar_g_vehicle_bumblebee_energy_regen) + self.vehicle_flags |= VHF_ENERGYREGEN; + + if(autocvar_g_vehicle_bumblebee_shield) + self.vehicle_flags |= VHF_HASSHIELD; + + if(autocvar_g_vehicle_bumblebee_shield_regen) + self.vehicle_flags |= VHF_SHIELDREGEN; + + if(autocvar_g_vehicle_bumblebee_health_regen) + self.vehicle_flags |= VHF_HEALTHREGEN; + + if not(vehicle_initialize( + "Bumblebee", "models/vehicles/bumblebee_body.dpm", + "", "models/vehicles/spiderbot_cockpit.dpm", "", "", "tag_viewport", + HUD_BUMBLEBEE, BUMB_MIN, BUMB_MAX, FALSE, + bumb_spawn, autocvar_g_vehicle_bumblebee_respawntime, + bumb_pilot_frame, bumb_enter, bumb_exit, + bumb_die, bumb_think, FALSE, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_shield)) + { + remove(self); + return; + } +} + +float bumble_raygun_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN); + + WriteByte(MSG_ENTITY, sf); + if(sf & BRG_SETUP) + { + WriteByte(MSG_ENTITY, num_for_edict(self.realowner)); + WriteByte(MSG_ENTITY, self.realowner.team); + WriteByte(MSG_ENTITY, self.cnt); + } + + if(sf & BRG_START) + { + WriteCoord(MSG_ENTITY, self.hook_start_x); + WriteCoord(MSG_ENTITY, self.hook_start_y); + WriteCoord(MSG_ENTITY, self.hook_start_z); + } + + if(sf & BRG_END) + { + WriteCoord(MSG_ENTITY, self.hook_end_x); + WriteCoord(MSG_ENTITY, self.hook_end_y); + WriteCoord(MSG_ENTITY, self.hook_end_z); + } + + return TRUE; +} +#endif // SVQC + +#ifdef CSQC +/* +.vector raygun_l1 +.vector raygun_l2; +.vector raygun_l3; +*/ + +void bumble_raygun_draw() +{ + float _len; + vector _dir; + vector _vtmp1, _vtmp2; + + _len = vlen(self.origin - self.move_origin); + _dir = normalize(self.move_origin - self.origin); + + if(self.total_damages < time) + { + boxparticles(self.traileffect, self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA); + boxparticles(self.lip, self, self.move_origin, self.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA); + self.total_damages = time + 0.1; + } + + float i, df, sz, al; + for(i = -0.1; i < 0.2; i += 0.1) + { + df = DRAWFLAG_NORMAL; //((random() < 0.5) ? DRAWFLAG_ADDITIVE : DRAWFLAG_SCREEN); + sz = 5 + random() * 5; + al = 0.25 + random() * 0.5; + _vtmp1 = self.origin + _dir * _len * (0.25 + i); + _vtmp1 += (randomvec() * (_len * 0.2) * (frametime * 2)); //self.raygun_l1; + Draw_CylindricLine(self.origin, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + + _vtmp2 = self.origin + _dir * _len * (0.5 + i); + _vtmp2 += (randomvec() * (_len * 0.2) * (frametime * 5)); //self.raygun_l2; + Draw_CylindricLine(_vtmp1, _vtmp2, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + + _vtmp1 = self.origin + _dir * _len * (0.75 + i); + _vtmp1 += randomvec() * (_len * 0.2) * (frametime * 10); //self.raygun_l3; + Draw_CylindricLine(_vtmp2, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + + Draw_CylindricLine(_vtmp1, self.move_origin + randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin); + } +} + +void bumble_raygun_read(float bIsNew) +{ + float sf = ReadByte(); + + if(sf & BRG_SETUP) + { + self.cnt = ReadByte(); + self.team = ReadByte(); + self.cnt = ReadByte(); + + if(self.cnt) + self.colormod = '1 0 0'; + else + self.colormod = '0 1 0'; + + self.traileffect = particleeffectnum("healray_muzzleflash"); + self.lip = particleeffectnum("healray_impact"); + + self.draw = bumble_raygun_draw; + } + + + if(sf & BRG_START) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + } + + if(sf & BRG_END) + { + self.move_origin_x = ReadCoord(); + self.move_origin_y = ReadCoord(); + self.move_origin_z = ReadCoord(); + } +} + +void bumblebee_draw() +{ + +} + +void bumblebee_draw2d() +{ + +} + +void bumblebee_read_extra() +{ + +} + +void vehicle_bumblebee_assemble() +{ + +} +#endif //CSQC diff --git a/qcsrc/server/vehicles/racer.qc b/qcsrc/server/vehicles/racer.qc index 777820e48c..792be58502 100644 --- a/qcsrc/server/vehicles/racer.qc +++ b/qcsrc/server/vehicles/racer.qc @@ -6,6 +6,8 @@ void racer_exit(float eject); void racer_enter(); // Auto cvars +float autocvar_g_vehicle_racer; + float autocvar_g_vehicle_racer_speed_afterburn; float autocvar_g_vehicle_racer_afterburn_cost; @@ -58,6 +60,7 @@ float autocvar_g_vehicle_racer_rocket_locking_time; float autocvar_g_vehicle_racer_rocket_locking_releasetime; float autocvar_g_vehicle_racer_rocket_locked_time; float autocvar_g_vehicle_racer_rocket_locked_maxangle; +float autocvar_g_vehicle_racer_rocket_climbspeed; float autocvar_g_vehicle_racer_respawntime; @@ -71,37 +74,34 @@ float autocvar_g_vehicle_racer_bouncestop; vector autocvar_g_vehicle_racer_bouncepain; var vector racer_force_from_tag(string tag_name, float spring_length, float max_power); +void racer_spawn(float _spawnflag); -void racer_align4point() +void racer_align4point(float _delta) { - vector push_vector, v_add; + vector push_vector; float fl_push, fr_push, bl_push, br_push; - push_vector = racer_force_from_tag("tag_engine_fr", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); fr_push = force_fromtag_normpower; - //vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier); + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); push_vector += racer_force_from_tag("tag_engine_fl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); fl_push = force_fromtag_normpower; - //vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier); + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); push_vector += racer_force_from_tag("tag_engine_br", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); br_push = force_fromtag_normpower; - //vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier); + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); push_vector += racer_force_from_tag("tag_engine_bl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower); bl_push = force_fromtag_normpower; - //vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier); + //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier); - self.velocity += (push_vector * frametime); + self.velocity += push_vector * _delta; // Anti ocilation - if(self.velocity_z > 0) - self.velocity_z *= 1 - (autocvar_g_vehicle_racer_upforcedamper * frametime); - - self.velocity += v_add; - //self.velocity_z -= autocvar_sv_gravity * frametime; + if(self.velocity_z > 0) + self.velocity_z *= 1 - autocvar_g_vehicle_racer_upforcedamper * _delta; push_vector_x = (fl_push - bl_push); push_vector_x += (fr_push - br_push); @@ -112,12 +112,12 @@ void racer_align4point() push_vector_z *= 360; // Apply angle diffrance - self.angles_z += push_vector_z * frametime; - self.angles_x += push_vector_x * frametime; + self.angles_z += push_vector_z * _delta; + self.angles_x += push_vector_x * _delta; // Apply stabilizer - self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * frametime); - self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * frametime); + self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta); + self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta); } void racer_fire_cannon(string tagname) @@ -129,7 +129,7 @@ void racer_fire_cannon(string tagname) bolt = vehicles_projectile("wakizashi_gun_muzzleflash", "weapons/lasergun_fire.wav", v, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed, autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force, 0, - DEATH_WAKIGUN, PROJECTILE_WAKICANNON, 0, TRUE, TRUE); + DEATH_WAKIGUN, PROJECTILE_WAKICANNON, 0, TRUE, TRUE, self.owner); // Fix z-aim (for chase mode) v = normalize(trace_endpos - bolt.origin); @@ -209,8 +209,14 @@ void racer_rocket_tracker() newvel = oldvel + self.lip; makevectors(vectoangles(olddir)); + float time_to_impact = min(vlen(self.enemy.origin - self.origin) / vlen(self.velocity), 1); + vector predicted_origin = self.enemy.origin + self.enemy.velocity * time_to_impact; + traceline(self.origin, self.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, self); - newdir = normalize(self.enemy.origin - self.origin); + newdir = normalize(predicted_origin - self.origin); + + //vector + float height_diff = predicted_origin_z - self.origin_z; if(vlen(newdir - v_forward) > autocvar_g_vehicle_racer_rocket_locked_maxangle) { @@ -220,11 +226,12 @@ void racer_rocket_tracker() return; } - if(trace_fraction != 1.0) + if(trace_fraction != 1.0 && trace_ent != self.enemy) newdir_z += 16 * sys_frametime; self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel; self.velocity_z -= 800 * sys_frametime; + self.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ; UpdateCSQCProjectile(self); return; @@ -232,14 +239,11 @@ void racer_rocket_tracker() void racer_fire_rocket(string tagname, entity trg) { - vector v; - entity rocket; - - v = gettaginfo(self, gettagindex(self, tagname)); - rocket = vehicles_projectile("wakizashi_rocket_launch", "weapons/rocket_fire.wav", + vector v = gettaginfo(self, gettagindex(self, tagname)); + 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; @@ -265,11 +269,11 @@ float racer_frame() player = self; racer = self.vehicle; self = racer; - + player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0; - + vehicles_painframe(); - + if(racer.deadflag != DEAD_NO) { self = player; @@ -277,7 +281,7 @@ float racer_frame() return 1; } - racer_align4point(); + racer_align4point(frametime); crosshair_trace(player); @@ -299,84 +303,68 @@ float racer_frame() makevectors(racer.angles); racer.angles_x *= -1; - - ftmp = racer.velocity_z; + //ftmp = racer.velocity_z; df = racer.velocity * -autocvar_g_vehicle_racer_friction; - racer.velocity_z = ftmp; + //racer.velocity_z = ftmp; - if(player.movement_x != 0) - { - if(player.movement_x > 0) - df += v_forward * autocvar_g_vehicle_racer_speed_forward; - else if(player.movement_x < 0) - df -= v_forward * autocvar_g_vehicle_racer_speed_forward; - } - - if(player.movement_y != 0) - { - if(player.movement_y < 0) - df -= v_right * autocvar_g_vehicle_racer_speed_strafe; - else if(player.movement_y > 0) - df += v_right * autocvar_g_vehicle_racer_speed_strafe; - } - if(vlen(player.movement) != 0) { + if(player.movement_x) + df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); + + if(player.movement_y) + df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); + if(self.sound_nexttime < time || self.sounds != 1) - { + { self.sounds = 1; self.sound_nexttime = time + 10.922667; //soundlength("vehicles/racer_move.wav"); sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_move.wav", VOL_VEHICLEENGINE, ATTN_NORM); } } else - { + { if(self.sound_nexttime < time || self.sounds != 0) - { + { self.sounds = 0; self.sound_nexttime = time + 11.888604; //soundlength("vehicles/racer_idle.wav"); sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_idle.wav", VOL_VEHICLEENGINE, ATTN_NORM); - } + } } - + // Afterburn 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; df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn); - - if(self.invincible_finished < time) - { - traceline(self.origin, self.origin - '0 0 256', MOVE_NORMAL, self); + + if(racer.invincible_finished < time) + { + traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self); if(trace_fraction != 1.0) pointparticles(particleeffectnum("smoke_small"), trace_endpos, '0 0 0', 1); - - self.invincible_finished = time + 0.1 + (random() * 0.1); + + racer.invincible_finished = time + 0.1 + (random() * 0.1); } - if(self.strength_finished < time) - { - //self.sounds = 2; - self.strength_finished = time + 10.922667; //soundlength("vehicles/racer_boost.wav"); - sound (self.tur_head, CH_TRIGGER_SINGLE, "vehicles/racer_boost.wav", VOL_VEHICLEENGINE, ATTN_NORM); - } + if(racer.strength_finished < time) + { + racer.strength_finished = time + 10.922667; //soundlength("vehicles/racer_boost.wav"); + sound (racer.tur_head, CH_TRIGGER_SINGLE, "vehicles/racer_boost.wav", VOL_VEHICLEENGINE, ATTN_NORM); + } } else { - self.strength_finished = 0; - sound (self.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTN_NORM); + racer.strength_finished = 0; + sound (racer.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTN_NORM); } - - racer.velocity += df * frametime; - - df = (vlen(racer.velocity) * autocvar_g_vehicle_racer_downforce * v_up) * frametime; - racer.velocity = racer.velocity - df; - player.movement = racer.velocity; + df -= v_up * (vlen(racer.velocity) * autocvar_g_vehicle_racer_downforce); + player.movement = racer.velocity += df * frametime; if(player.BUTTON_ATCK) if(time > racer.attack_finished_single) @@ -397,7 +385,6 @@ float racer_frame() racer.cnt = 1; } racer.attack_finished_single = time + autocvar_g_vehicle_racer_cannon_refire; - //self.owner.vehicle_energy = racer.vehicle_energy / autocvar_g_vehicle_racer_energy; } if(autocvar_g_vehicle_racer_rocket_locktarget) @@ -420,16 +407,17 @@ float racer_frame() if(time > racer.delay) if(player.BUTTON_ATCK2) { - self.misc_bulletcounter += 1; + racer.misc_bulletcounter += 1; racer.delay = time + 0.3; - if(self.misc_bulletcounter == 1) - racer_fire_rocket("tag_rocket_r", (self.lock_strength == 1 && self.lock_target) ? self.lock_target : world); - else if(self.misc_bulletcounter == 2) + + if(racer.misc_bulletcounter == 1) + racer_fire_rocket("tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); + else if(racer.misc_bulletcounter == 2) { - racer_fire_rocket("tag_rocket_l", (self.lock_strength == 1 && self.lock_target) ? self.lock_target : world); - self.lock_strength = 0; - self.lock_target = world; - self.misc_bulletcounter = 0; + racer_fire_rocket("tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); + racer.lock_strength = 0; + racer.lock_target = world; + racer.misc_bulletcounter = 0; racer.delay = time + autocvar_g_vehicle_racer_rocket_refire; racer.lip = time; @@ -437,22 +425,21 @@ float racer_frame() } player.vehicle_reload1 = bound(0, 100 * ((time - racer.lip) / (racer.delay - racer.lip)), 100); - if(self.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); + if(racer.vehicle_flags & VHF_SHIELDREGEN) + 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(self.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); + if(racer.vehicle_flags & VHF_HEALTHREGEN) + 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(self.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); + if(racer.vehicle_flags & VHF_ENERGYREGEN) + 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); - - if(self.vehicle_flags & VHF_HASSHIELD) - VEHICLE_UPDATE_PLAYER(shield, racer); + VEHICLE_UPDATE_PLAYER(player, health, racer); + VEHICLE_UPDATE_PLAYER(player, energy, racer); + if(racer.vehicle_flags & VHF_HASSHIELD) + VEHICLE_UPDATE_PLAYER(player, shield, racer); player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0; setorigin(player,racer.origin + '0 0 32'); @@ -464,32 +451,24 @@ float racer_frame() void racer_think() { - /* - float a, b, c;a = autocvar_g_vehicle_racer_anglestabilizer; - b = autocvar_g_vehicle_racer_springlength; - c = autocvar_g_vehicle_racer_hoverpower; - - autocvar_g_vehicle_racer_anglestabilizer = 36; - autocvar_g_vehicle_racer_springlength = 96; - autocvar_g_vehicle_racer_hoverpower = 300; - */ - - racer_align4point(); //time - self.nextthink); - - /* - //if(self.velocity_z > 0) - // self.velocity_z *= 0.95; - - autocvar_g_vehicle_racer_anglestabilizer = a; - autocvar_g_vehicle_racer_springlength = b; - autocvar_g_vehicle_racer_hoverpower = c; - */ - - self.velocity_x *= 0.9; - self.velocity_y *= 0.9; - self.velocity_z *= 0.8; - self.velocity_z += sin(time * 2) * 16; - self.nextthink = time; // + 0.05; + self.nextthink = time; + + float pushdeltatime = time - self.lastpushtime; + if (pushdeltatime > 0.15) pushdeltatime = 0; + self.lastpushtime = time; + if(!pushdeltatime) return; + + tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NORMAL, self); + + vector df = self.velocity * -autocvar_g_vehicle_racer_friction; + df_z += (1 - trace_fraction) * autocvar_g_vehicle_racer_hoverpower + sin(time * 2) * (autocvar_g_vehicle_racer_springlength * 2); + + self.velocity += df * pushdeltatime; + if(self.velocity_z > 0) + self.velocity_z *= 1 - autocvar_g_vehicle_racer_upforcedamper * pushdeltatime; + + self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime); + self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime); } void racer_enter() @@ -497,20 +476,22 @@ void racer_enter() self.movetype = MOVETYPE_BOUNCE; self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_racer_health) * 100; self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_racer_shield) * 100; - + if(self.owner.flagcarried) setorigin(self.owner.flagcarried, '-190 0 96'); + + //targetdrone_spawn(self.origin + '0 0 512' + randomvec() * 256, 1); } void racer_exit(float eject) { vector spot; - + self.think = racer_think; self.nextthink = time; - self.movetype = MOVETYPE_TOSS; + self.movetype = MOVETYPE_BOUNCE; sound (self.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTN_NORM); - + if not (self.owner) return; @@ -525,47 +506,39 @@ void racer_exit(float eject) } else { - self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed; + if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed) + { + self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2; + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 32 + '0 0 32'; + 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 32'; + 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; } + void racer_impact() { if(autocvar_g_vehicle_racer_bouncepain_x) 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.vehicle_exit(VHEF_NORMAL); - RadiusDamage (self, self, autocvar_g_vehicle_racer_blowup_coredamage, + RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage, autocvar_g_vehicle_racer_blowup_edgedamage, autocvar_g_vehicle_racer_blowup_radius, world, autocvar_g_vehicle_racer_blowup_forceintensity, @@ -604,8 +577,8 @@ void racer_die() self.wait = time; self.cnt = 1 + random() * 2; self.touch = racer_deadtouch; - - pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1); + + pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1); if(random() < 0.5) self.avelocity_z = 32; @@ -619,49 +592,57 @@ void racer_die() self.think = racer_blowup; self.nextthink = 2 + time + random() * 3; } - -void racer_dinit() +void racer_spawn(float _spawnflag) { - if not (vehicle_initialize( - "Wakizashi", - "models/vehicles/wakizashi.dpm", - "null", // we need this so tur_head is networked and usable for sounds - "models/vehicles/wakizashi_cockpit.dpm", - "", "", "tag_viewport", - HUD_WAKIZASHI, - 0.5 * RACER_MIN, 0.5 * RACER_MAX, - FALSE, - racer_spawn, autocvar_g_vehicle_racer_respawntime, - racer_frame, - racer_enter, racer_exit, - racer_die, racer_think, - TRUE)) + 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_sound ("weapons/lasergun_fire.wav"); precache_sound ("weapons/rocket_fire.wav"); - + precache_sound ("vehicles/racer_idle.wav"); precache_sound ("vehicles/racer_move.wav"); precache_sound ("vehicles/racer_boost.wav"); @@ -670,7 +651,6 @@ void spawnfunc_vehicle_racer() precache_model ("models/vehicles/wakizashi.dpm"); precache_model ("models/vehicles/wakizashi_cockpit.dpm"); - vehicles_configcheck("vehicle_racer.cfg", autocvar_g_vehicle_racer_health); if(autocvar_g_vehicle_racer_energy) if(autocvar_g_vehicle_racer_energy_regen) self.vehicle_flags |= VHF_ENERGYREGEN; @@ -684,11 +664,25 @@ void spawnfunc_vehicle_racer() if(autocvar_g_vehicle_racer_health_regen) self.vehicle_flags |= VHF_HEALTHREGEN; - self.think = racer_dinit; - - if(g_assault) - self.nextthink = time + 0.5; - else - self.nextthink = time + (autocvar_g_vehicles_delayspawn ? autocvar_g_vehicle_racer_respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter) : 0.5); + if not (vehicle_initialize( + "Wakizashi", + "models/vehicles/wakizashi.dpm", + "null", // we need this so tur_head is networked and usable for sounds + "models/vehicles/wakizashi_cockpit.dpm", + "", "", "tag_viewport", + HUD_WAKIZASHI, + 0.5 * RACER_MIN, 0.5 * RACER_MAX, + FALSE, + racer_spawn, autocvar_g_vehicle_racer_respawntime, + racer_frame, + racer_enter, racer_exit, + racer_die, racer_think, + TRUE, + autocvar_g_vehicle_racer_health, + autocvar_g_vehicle_racer_shield)) + { + remove(self); + return; + } } #endif // SVQC diff --git a/qcsrc/server/vehicles/raptor.qc b/qcsrc/server/vehicles/raptor.qc index ab0d388155..a623bd5407 100644 --- a/qcsrc/server/vehicles/raptor.qc +++ b/qcsrc/server/vehicles/raptor.qc @@ -1,8 +1,16 @@ -#ifdef SVQC +#define RSM_FIRST 0 +#define RSM_BOMB 0 +#define RSM_FLARE 1 +#define RSM_LAST 1 + #define RAPTOR_MIN '-80 -80 0' #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_movestyle; float autocvar_g_vehicle_raptor_turnspeed; @@ -26,6 +34,11 @@ float autocvar_g_vehicle_raptor_bomblet_force; float autocvar_g_vehicle_raptor_bomblet_explode_delay; float autocvar_g_vehicle_raptor_bombs_refire; +float autocvar_g_vehicle_raptor_flare_refire; +float autocvar_g_vehicle_raptor_flare_lifetime; +float autocvar_g_vehicle_raptor_flare_chase; +float autocvar_g_vehicle_raptor_flare_range; + float autocvar_g_vehicle_raptor_cannon_turnspeed; float autocvar_g_vehicle_raptor_cannon_turnlimit; float autocvar_g_vehicle_raptor_cannon_pitchlimit_up; @@ -61,7 +74,7 @@ float autocvar_g_vehicle_raptor_bouncefactor; float autocvar_g_vehicle_raptor_bouncestop; vector autocvar_g_vehicle_raptor_bouncepain; -void raptor_spawn(); +void raptor_spawn(float); float raptor_frame(); float raptor_takeoff(); @@ -172,7 +185,7 @@ void raptor_fire_cannon(entity gun, string tagname) vehicles_projectile("raptor_cannon_muzzleflash", "weapons/lasergun_fire.wav", gettaginfo(gun, gettagindex(gun, tagname)), normalize(v_forward + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed, autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force, 0, - DEATH_RAPTOR_CANNON, PROJECTILE_RAPTORCANNON, 0, TRUE, TRUE); + DEATH_RAPTOR_CANNON, PROJECTILE_RAPTORCANNON, 0, TRUE, TRUE, self.owner); } void raptor_think() @@ -181,6 +194,7 @@ void raptor_think() void raptor_enter() { + self.vehicle_weapon2mode = RSM_BOMB; self.owner.PlayerPhysplug = raptor_takeoff; self.movetype = MOVETYPE_BOUNCEMISSILE; self.solid = SOLID_SLIDEBOX; @@ -194,7 +208,8 @@ void raptor_enter() if(self.owner.flagcarried) setorigin(self.owner.flagcarried, '-20 0 96'); - + + CSQCVehicleSetup(self.owner, 0); } void raptor_land() @@ -236,7 +251,7 @@ void raptor_exit(float eject) if not (self.owner) return; - + makevectors(self.angles); if(eject) { @@ -248,12 +263,24 @@ void raptor_exit(float eject) } else { - self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed; + if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed) + { + self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2; + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 32 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin - v_forward * 200 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } self.owner.oldvelocity = self.owner.velocity; - spot = self.origin - v_forward * 200 + '0 0 64'; - spot = vehicles_findgoodexit(spot); setorigin(self.owner , spot); } + antilag_clear(self.owner); self.owner = world; } @@ -274,7 +301,7 @@ float raptor_takeoff() // Takeoff sequense if(raptor.frame < 25) { - raptor.frame += 0.25; + raptor.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime); raptor.velocity_z = min(raptor.velocity_z * 1.5, 256); self.bomb1.gun1.avelocity_y = 90 + ((raptor.frame / 25) * 25000); self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y; @@ -286,32 +313,61 @@ float raptor_takeoff() player.PlayerPhysplug = raptor_frame; if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime); + 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); + 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); + 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; return 1; } +void raptor_flare_touch() +{ + remove(self); +} + +void raptor_flare_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + self.health -= damage; + if(self.health <= 0) + remove(self); +} + +void raptor_flare_think() +{ + self.nextthink = time + 0.1; + entity _missile = findchainentity(enemy, self.owner); + while(_missile) + { + if(_missile.flags & FL_PROJECTILE) + if(vlen(self.origin - _missile.origin) < autocvar_g_vehicle_raptor_flare_range) + if(random() > autocvar_g_vehicle_raptor_flare_chase) + _missile.enemy = self; + _missile = _missile.chain; + } + + if(self.tur_impacttime < time) + remove(self); +} + float raptor_frame() { entity player, raptor; - float ftmp, ftmp2; + float ftmp = 0; vector df; if(intermission_running) @@ -422,7 +478,57 @@ float raptor_frame() vector vf, ad; // Target lock & predict - if(autocvar_g_vehicle_raptor_cannon_locktarget) + if(autocvar_g_vehicle_raptor_cannon_locktarget == 2) + { + if(raptor.gun1.lock_time < time || raptor.gun1.enemy.deadflag) + raptor.gun1.enemy = world; + + if(trace_ent) + if(trace_ent.movetype) + if(trace_ent.takedamage) + if(!trace_ent.deadflag) + { + if(teamplay) + { + if(trace_ent.team != player.team) + { + raptor.gun1.enemy = trace_ent; + raptor.gun1.lock_time = time + 5; + } + } + else + { + raptor.gun1.enemy = trace_ent; + raptor.gun1.lock_time = time + 0.5; + } + } + + if(raptor.gun1.enemy) + { + float i, distance, impact_time; + + vf = real_origin(raptor.gun1.enemy); + UpdateAuxiliaryXhair(player, vf, '1 0 0', 1); + vector _vel = raptor.gun1.enemy.velocity; + if(raptor.gun1.enemy.movetype == MOVETYPE_WALK) + _vel_z *= 0.1; + + if(autocvar_g_vehicle_raptor_cannon_predicttarget) + { + ad = vf; + for(i = 0; i < 4; ++i) + { + distance = vlen(ad - player.origin); + impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed; + ad = vf + _vel * impact_time; + } + trace_endpos = ad; + } + else + trace_endpos = vf; + } + } + else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1) { vehicles_locktarget((1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime, @@ -457,39 +563,14 @@ float raptor_frame() } } - // Aim the gunz - ftmp2 = autocvar_g_vehicle_raptor_cannon_turnspeed * frametime; - ftmp = -ftmp2; - - // Gun1 - df = gettaginfo(raptor.gun1, gettagindex(raptor.gun1, "fire1")); - //ad = df; - //vf = v_forward; - df = vectoangles(normalize(trace_endpos - df)); // Find the direction & angle - df = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(raptor.angles), AnglesTransform_FromAngles(df))) - raptor.gun1.angles; - df = shortangle_vxy(df, raptor.gun1.angles); - - // Bind to aimspeed - df_x = bound(ftmp, df_x, ftmp2); - df_y = bound(ftmp, df_y, ftmp2); - // Bind to limts - raptor.gun1.angles_x = bound(-autocvar_g_vehicle_raptor_cannon_pitchlimit_down, df_x + raptor.gun1.angles_x, autocvar_g_vehicle_raptor_cannon_pitchlimit_up); - raptor.gun1.angles_y = bound(-autocvar_g_vehicle_raptor_cannon_turnlimit, df_y + raptor.gun1.angles_y, autocvar_g_vehicle_raptor_cannon_turnlimit); - - // Gun2 - df = gettaginfo(raptor.gun2, gettagindex(raptor.gun2, "fire1")); - //ad += df; - //vf += v_forward; - df = vectoangles(normalize(trace_endpos - df)); // Find the direction & angle - df = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(raptor.angles), AnglesTransform_FromAngles(df))) - raptor.gun2.angles; - df = shortangle_vxy(df, raptor.gun2.angles); - - // Bind to aimspeed - df_x = bound(ftmp, df_x, ftmp2); - df_y = bound(ftmp, df_y, ftmp2); - // Bind to limts - raptor.gun2.angles_x = bound(-autocvar_g_vehicle_raptor_cannon_pitchlimit_down, df_x + raptor.gun2.angles_x, autocvar_g_vehicle_raptor_cannon_pitchlimit_up); - raptor.gun2.angles_y = bound(-autocvar_g_vehicle_raptor_cannon_turnlimit, df_y + raptor.gun2.angles_y, autocvar_g_vehicle_raptor_cannon_turnlimit); + + vehicle_aimturret(raptor, trace_endpos, raptor.gun1, "fire1", + autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up, + autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed); + + vehicle_aimturret(raptor, trace_endpos, raptor.gun2, "fire1", + autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up, + autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed); /* ad = ad * 0.5; @@ -519,30 +600,85 @@ float raptor_frame() } if(self.vehicle_flags & VHF_SHIELDREGEN) - vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime); + 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); + 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); + 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(time > raptor.delay) - if(player.BUTTON_ATCK2) + if(raptor.vehicle_weapon2mode == RSM_BOMB) { - raptor_bombdrop(); - raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire; - raptor.lip = time; + if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire) + if(player.BUTTON_ATCK2) + { + raptor_bombdrop(); + raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire; + raptor.lip = time; + } } - + else + { + if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire) + if(player.BUTTON_ATCK2) + { + float i; + entity _flare; + + for(i = 0; i < 3; ++i) + { + _flare = spawn(); + setmodel(_flare, "models/runematch/rune.mdl"); + _flare.effects = EF_LOWPRECISION | EF_FLAME; + _flare.scale = 0.5; + setorigin(_flare, self.origin - '0 0 16'); + _flare.movetype = MOVETYPE_TOSS; + _flare.gravity = 0.15; + _flare.velocity = 0.25 * raptor.velocity + (v_forward + randomvec() * 0.25)* -500; + _flare.think = raptor_flare_think; + _flare.nextthink = time; + _flare.owner = raptor; + _flare.solid = SOLID_CORPSE; + _flare.takedamage = DAMAGE_YES; + _flare.event_damage = raptor_flare_damage; + _flare.health = 20; + _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime; + _flare.touch = raptor_flare_touch; + } + raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire; + raptor.lip = time; + } + } + 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); + if(self.bomb1.cnt < time) + { + entity _missile = findchainentity(enemy, raptor); + float _incomming = 0; + while(_missile) + { + if(_missile.flags & FL_PROJECTILE) + if(MISSILE_IS_TRACKING(_missile)) + if(vlen(self.origin - _missile.origin) < 2 * autocvar_g_vehicle_raptor_flare_range) + ++_incomming; + + _missile = _missile.chain; + } + + if(_incomming) + sound(self, CH_PAIN_SINGLE, "vehicles/missile_alarm.wav", VOL_BASE, ATTN_NONE); + + self.bomb1.cnt = time + 1; + } + + + 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; @@ -554,7 +690,7 @@ void raptor_blowup() { self.deadflag = DEAD_DEAD; self.vehicle_exit(VHEF_NORMAL); - RadiusDamage (self, self, 250, 15, 250, world, 250, DEATH_WAKIBLOWUP, world); + RadiusDamage (self, self.enemy, 250, 15, 250, world, 250, DEATH_WAKIBLOWUP, world); self.alpha = -1; self.movetype = MOVETYPE_NONE; @@ -570,6 +706,9 @@ void raptor_blowup() void raptor_diethink() { + if(time >= self.wait) + self.think = raptor_blowup; + if(random() < 0.1) { sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); @@ -588,6 +727,7 @@ void raptor_die() self.movetype = MOVETYPE_BOUNCE; self.think = raptor_diethink; self.nextthink = time; + self.wait = time + 5 + (random() * 5); pointparticles(particleeffectnum("explosion_medium"), findbetterlocation (self.origin, 16), '0 0 0', 1); @@ -606,8 +746,120 @@ void raptor_impact() vehilces_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z); } -void raptor_spawn() +// 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.gun1.angles_y = anglemods(self.gun1.angles_y); + self.gun2.angles_y = anglemods(self.gun2.angles_y); + self.nextthink = time + 15; +} + +float raptor_impulse(float _imp) +{ + switch(_imp) + { + case 10: + case 15: + case 18: + self.vehicle.vehicle_weapon2mode += 1; + if(self.vehicle.vehicle_weapon2mode > RSM_LAST) + self.vehicle.vehicle_weapon2mode = RSM_FIRST; + + CSQCVehicleSetup(self, 0); + return TRUE; + case 12: + case 16: + case 19: + self.vehicle.vehicle_weapon2mode -= 1; + if(self.vehicle.vehicle_weapon2mode < RSM_FIRST) + self.vehicle.vehicle_weapon2mode = RSM_LAST; + + CSQCVehicleSetup(self, 0); + return TRUE; + + /* + case 17: // toss gun, could be used to exit? + break; + case 20: // Manual minigun reload? + break; + */ + } + return FALSE; +} + +void raptor_spawn(float _f) { + if(!self.gun1) + { + 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 ; + } + + self.frame = 0; self.vehicle_health = autocvar_g_vehicle_raptor_health; self.vehicle_shield = autocvar_g_vehicle_raptor_shield; @@ -624,108 +876,17 @@ void raptor_spawn() self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor; self.bouncestop = autocvar_g_vehicle_raptor_bouncestop; self.vehicle_impact = raptor_impact; + self.damageforcescale = 0.25; } -// 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.gun1.angles_y = anglemods(self.gun1.angles_y); - self.gun2.angles_y = anglemods(self.gun2.angles_y); - self.nextthink = time + 15; -} - -void raptor_dinit() +void spawnfunc_vehicle_raptor() { - 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)) + if(!autocvar_g_vehicle_raptor) { remove(self); return; - } - - //FIXME: Camera is in a bad place in HUD model. - //setorigin(self.vehicle_viewport, '25 0 5'); - - 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"); - - - // FIXME 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 ; -} - -void spawnfunc_vehicle_raptor() -{ - vehicles_configcheck("vehicle_raptor.cfg", autocvar_g_vehicle_raptor_health); - + } + self.vehicle_flags |= VHF_DMGSHAKE; self.vehicle_flags |= VHF_DMGROLL; @@ -751,12 +912,29 @@ void spawnfunc_vehicle_raptor() precache_sound ("vehicles/raptor_fly.wav"); precache_sound ("vehicles/raptor_speed.wav"); + precache_sound ("vehicles/missile_alarm.wav"); + + 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; + } - self.think = raptor_dinit; - 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 diff --git a/qcsrc/server/vehicles/spiderbot.qc b/qcsrc/server/vehicles/spiderbot.qc index 913e3316f0..71e57a037f 100644 --- a/qcsrc/server/vehicles/spiderbot.qc +++ b/qcsrc/server/vehicles/spiderbot.qc @@ -2,21 +2,24 @@ const vector SPIDERBOT_MIN = '-75 -75 10'; const vector SPIDERBOT_MAX = '75 75 125'; #ifdef SVQC +float autocvar_g_vehicle_spiderbot; + float autocvar_g_vehicle_spiderbot_respawntime; float autocvar_g_vehicle_spiderbot_speed_stop; float autocvar_g_vehicle_spiderbot_speed_strafe; float autocvar_g_vehicle_spiderbot_speed_walk; float autocvar_g_vehicle_spiderbot_turnspeed; +float autocvar_g_vehicle_spiderbot_turnspeed_strafe; float autocvar_g_vehicle_spiderbot_movement_inertia; float autocvar_g_vehicle_spiderbot_springlength; float autocvar_g_vehicle_spiderbot_springup; float autocvar_g_vehicle_spiderbot_springblend; +float autocvar_g_vehicle_spiderbot_tiltlimit; float autocvar_g_vehicle_spiderbot_head_pitchlimit_down; float autocvar_g_vehicle_spiderbot_head_pitchlimit_up; -float autocvar_g_vehicle_spiderbot_head_pitchspeed; float autocvar_g_vehicle_spiderbot_head_turnlimit; float autocvar_g_vehicle_spiderbot_head_turnspeed; @@ -39,12 +42,17 @@ float autocvar_g_vehicle_spiderbot_minigun_ammo_cost; float autocvar_g_vehicle_spiderbot_minigun_ammo_max; float autocvar_g_vehicle_spiderbot_minigun_ammo_regen; float autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause; +float autocvar_g_vehicle_spiderbot_minigun_force; +float autocvar_g_vehicle_spiderbot_minigun_speed; +float autocvar_g_vehicle_spiderbot_minigun_bulletconstant; float autocvar_g_vehicle_spiderbot_rocket_damage; float autocvar_g_vehicle_spiderbot_rocket_force; float autocvar_g_vehicle_spiderbot_rocket_radius; float autocvar_g_vehicle_spiderbot_rocket_speed; +float autocvar_g_vehicle_spiderbot_rocket_spread; float autocvar_g_vehicle_spiderbot_rocket_refire; +float autocvar_g_vehicle_spiderbot_rocket_refire2; float autocvar_g_vehicle_spiderbot_rocket_reload; float autocvar_g_vehicle_spiderbot_rocket_health; float autocvar_g_vehicle_spiderbot_rocket_noise; @@ -56,7 +64,18 @@ vector autocvar_g_vehicle_spiderbot_bouncepain; void spiderbot_exit(float eject); void spiderbot_enter(); -void spiderbot_spawn(); +void spiderbot_spawn(float); +#define SBRM_FIRST 0 +#define SBRM_VOLLY 0 +#define SBRM_GUIDE 1 +#define SBRM_ARTILLERY 2 +#define SBRM_LAST 2 + +void spiderbot_rocket_artillery() +{ + self.nextthink = time; + UpdateCSQCProjectile(self); +} void spiderbot_rocket_unguided() { @@ -113,65 +132,196 @@ void spiderbot_guide_release() } } +float spiberbot_calcartillery_flighttime; +vector spiberbot_calcartillery(vector org, vector tgt, float ht) +{ + float grav, sdist, zdist, vs, vz, jumpheight; + vector sdir; + + grav = autocvar_sv_gravity; + zdist = tgt_z - org_z; + sdist = vlen(tgt - org - zdist * '0 0 1'); + sdir = normalize(tgt - org - zdist * '0 0 1'); + + // how high do we need to go? + jumpheight = fabs(ht); + if(zdist > 0) + jumpheight = jumpheight + zdist; + + // push so high... + vz = sqrt(2 * grav * jumpheight); // NOTE: sqrt(positive)! + + // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump! + if(ht < 0) + if(zdist < 0) + vz = -vz; + + vector solution; + solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist" + // ALWAYS solvable because jumpheight >= zdist + if(!solution_z) + solution_y = solution_x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) + if(zdist == 0) + solution_x = solution_y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) + + if(zdist < 0) + { + // down-jump + if(ht < 0) + { + // almost straight line type + // jump apex is before the jump + // we must take the larger one + spiberbot_calcartillery_flighttime = solution_y; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one too + spiberbot_calcartillery_flighttime = solution_y; + } + } + else + { + // up-jump + if(ht < 0) + { + // almost straight line type + // jump apex is after the jump + // we must take the smaller one + spiberbot_calcartillery_flighttime = solution_x; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one + spiberbot_calcartillery_flighttime = solution_y; + } + } + vs = sdist / spiberbot_calcartillery_flighttime; + + // finally calculate the velocity + return sdir * vs + '0 0 1' * vz; +} + void spiderbot_rocket_do() { vector v; entity rocket; - if (self.owner.BUTTON_ATCK2) - { - if (self.wait == 1) - if (self.tur_head.frame == 9 || self.tur_head.frame == 1) + if (self.wait != -10) + { + if (self.owner.BUTTON_ATCK2 && self.vehicle_weapon2mode == SBRM_GUIDE) { - if(self.gun2.cnt < time && self.tur_head.frame == 9) - self.tur_head.frame = 1; + if (self.wait == 1) + if (self.tur_head.frame == 9 || self.tur_head.frame == 1) + { + if(self.gun2.cnt < time && self.tur_head.frame == 9) + self.tur_head.frame = 1; - return; + return; + } + self.wait = 1; } - self.wait = 1; - } - else - { - if(self.wait) - spiderbot_guide_release(); + else + { + if(self.wait) + spiderbot_guide_release(); - self.wait = 0; + self.wait = 0; + } } - + if(self.gun2.cnt > time) return; if (self.tur_head.frame >= 9) + { self.tur_head.frame = 1; + self.wait = 0; + } + + if (self.wait != -10) + if not (self.owner.BUTTON_ATCK2) + return; - if not (self.owner.BUTTON_ATCK2) - return; - - crosshair_trace(self.owner); v = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire")); - 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); - - rocket.cnt = time + 15; + + switch(self.vehicle_weapon2mode) + { + case SBRM_VOLLY: + 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, 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.nextthink = time + (_dist / autocvar_g_vehicle_spiderbot_rocket_speed); + rocket.think = vehicles_projectile_explode; + + if(self.owner.BUTTON_ATCK2 && self.tur_head.frame == 1) + self.wait = -10; + break; + case SBRM_GUIDE: + 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, self.owner); + crosshair_trace(self.owner); + rocket.pos1 = trace_endpos; + rocket.nextthink = time; + rocket.think = spiderbot_rocket_guided; + + + break; + case SBRM_ARTILLERY: + 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, 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); + 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; + break; + } rocket.classname = "spiderbot_rocket"; - rocket.pos1 = trace_endpos; - rocket.think = spiderbot_rocket_guided; - rocket.nextthink = time; - rocket.cnt = time + autocvar_g_vehicle_spiderbot_rocket_lifetime; - + + rocket.cnt = time + autocvar_g_vehicle_spiderbot_rocket_lifetime; + self.tur_head.frame += 1; if (self.tur_head.frame == 9) self.attack_finished_single = autocvar_g_vehicle_spiderbot_rocket_reload; else - self.attack_finished_single = autocvar_g_vehicle_spiderbot_rocket_refire; + self.attack_finished_single = ((self.vehicle_weapon2mode == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire); self.gun2.cnt = time + self.attack_finished_single; } +float spiderbot_aiframe() +{ + return FALSE; +} + float spiderbot_frame() { vector ad, vf; @@ -190,6 +340,7 @@ float spiderbot_frame() player.BUTTON_ZOOM = 0; player.BUTTON_CROUCH = 0; player.switchweapon = 0; + #if 1 // 0 to enable per-gun impact aux crosshairs // Avarage gun impact point's -> aux cross @@ -219,23 +370,19 @@ float spiderbot_frame() //UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload2) + ('0 1 0' * (1 - player.vehicle_reload2)), 2); // Rotate head - ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime; + ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime; ad_y = bound(-ftmp, ad_y, ftmp); spider.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, spider.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit); // Pitch head - ftmp = autocvar_g_vehicle_spiderbot_head_pitchspeed * sys_frametime; ad_x = bound(ftmp * -1, ad_x, ftmp); spider.tur_head.angles_x = bound(autocvar_g_vehicle_spiderbot_head_pitchlimit_down, spider.tur_head.angles_x + ad_x, autocvar_g_vehicle_spiderbot_head_pitchlimit_up); - // Turn Body - ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime; - ftmp = bound(-ftmp, spider.tur_head.angles_y, ftmp); //fixedmakevectors(spider.angles); makevectors(spider.angles + '-2 0 0' * spider.angles_x); - movelib_groundalign4point(autocvar_g_vehicle_spiderbot_springlength, autocvar_g_vehicle_spiderbot_springup, autocvar_g_vehicle_spiderbot_springblend); + movelib_groundalign4point(autocvar_g_vehicle_spiderbot_springlength, autocvar_g_vehicle_spiderbot_springup, autocvar_g_vehicle_spiderbot_springblend, autocvar_g_vehicle_spiderbot_tiltlimit); if(spider.flags & FL_ONGROUND) { @@ -272,6 +419,13 @@ float spiderbot_frame() } else { + // Turn Body + if(player.movement_x == 0 && player.movement_y != 0) + ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime; + else + ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime; + + ftmp = bound(-ftmp, spider.tur_head.angles_y, ftmp); spider.angles_y = anglemods(spider.angles_y + ftmp); spider.tur_head.angles_y -= ftmp; @@ -323,8 +477,8 @@ float spiderbot_frame() } } - self.angles_x = bound(-45, self.angles_x, 45); - self.angles_z = bound(-45, self.angles_z, 45); + self.angles_x = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_x, autocvar_g_vehicle_spiderbot_tiltlimit); + self.angles_z = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_z, autocvar_g_vehicle_spiderbot_tiltlimit); if(player.BUTTON_ATCK) { @@ -342,11 +496,17 @@ float spiderbot_frame() v_forward = normalize(v_forward); v += v_forward * 50; - fireBullet (v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_damage, - autocvar_g_vehicle_spiderbot_minigun_spread, DEATH_SBMINIGUN, 0); +//void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant) + + fireBallisticBullet(v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_speed, + 5, autocvar_g_vehicle_spiderbot_minigun_damage, 0, autocvar_g_vehicle_spiderbot_minigun_force, DEATH_SBMINIGUN, 0, 1, autocvar_g_vehicle_spiderbot_minigun_bulletconstant); + endFireBallisticBullet(); + +// fireBullet (v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_damage, +// autocvar_g_vehicle_spiderbot_minigun_spread, DEATH_SBMINIGUN, 0); sound (gun, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM); - trailparticles(self, particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos); + //trailparticles(self, particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos); pointparticles(particleeffectnum("spiderbot_minigun_muzzleflash"), v, v_forward * 2500, 1); self = spider; @@ -364,18 +524,18 @@ float spiderbot_frame() } } else - vehicles_regen(cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max, + vehicles_regen(spider.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max, autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause, - autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime); + 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); + 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); + 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; @@ -388,10 +548,10 @@ float spiderbot_frame() setorigin(player, spider.origin + '0 0 1' * SPIDERBOT_MAX_z); player.velocity = spider.velocity; - VEHICLE_UPDATE_PLAYER(health, spiderbot); + VEHICLE_UPDATE_PLAYER(player, health, spiderbot); if(self.vehicle_flags & VHF_HASSHIELD) - VEHICLE_UPDATE_PLAYER(shield, spiderbot); + VEHICLE_UPDATE_PLAYER(player, shield, spiderbot); self = player; return 1; @@ -406,8 +566,9 @@ void spiderbot_think() void spiderbot_enter() { + self.vehicle_weapon2mode = SBRM_GUIDE; self.movetype = MOVETYPE_WALK; - + CSQCVehicleSetup(self.owner, 0); self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100; self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100; @@ -434,7 +595,7 @@ void spiderbot_exit(float eject) e = e.chain; } - self.velocity = '0 0 0'; + //self.velocity = '0 0 0'; self.think = spiderbot_think; self.nextthink = time; self.frame = 5; @@ -454,37 +615,33 @@ void spiderbot_exit(float eject) } else { - self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed; - self.owner.oldvelocity = self.owner.velocity; - spot = self.origin - v_forward * 200 + '0 0 64'; - spot = vehicles_findgoodexit(spot); + if(vlen(self.velocity) > autocvar_g_vehicle_spiderbot_speed_strafe) + { + self.owner.velocity = normalize(self.velocity) * vlen(self.velocity); + self.owner.velocity_z += 200; + spot = self.origin + v_forward * 128 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + else + { + self.owner.velocity = self.velocity * 0.5; + self.owner.velocity_z += 10; + spot = self.origin + v_forward * 256 + '0 0 64'; + spot = vehicles_findgoodexit(spot); + } + self.owner.oldvelocity = self.owner.velocity; setorigin(self.owner , spot); } - antilag_clear(self.owner); + + antilag_clear(self.owner); self.owner = world; } + void spider_impact() { if(autocvar_g_vehicle_spiderbot_bouncepain_x) vehilces_impact(autocvar_g_vehicle_spiderbot_bouncepain_x, autocvar_g_vehicle_spiderbot_bouncepain_y, autocvar_g_vehicle_spiderbot_bouncepain_z); } -void spiderbot_spawn() -{ - self.frame = 5; - self.tur_head.frame = 1; - self.think = spiderbot_think; - self.nextthink = time; - self.vehicle_health = autocvar_g_vehicle_spiderbot_health; - self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield; - self.movetype = MOVETYPE_WALK; - self.solid = SOLID_SLIDEBOX; - self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1; - self.tur_head.angles = '0 0 0'; - - setorigin(self, self.pos1 + '0 0 128'); - self.angles = self.pos2; - self.vehicle_impact = spider_impact; -} void spiderbot_headfade() { @@ -566,7 +723,7 @@ void spiderbot_blowup() SUB_SetFade(g1, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10)); SUB_SetFade(g2, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10)); - RadiusDamage (self, self, 250, 15, 250, world, 250, DEATH_SBBLOWUP, world); + RadiusDamage (self, self.enemy, 250, 15, 250, world, 250, DEATH_SBBLOWUP, world); self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1; self.movetype = MOVETYPE_NONE; @@ -593,43 +750,81 @@ void spiderbot_die() self.movetype = MOVETYPE_TOSS; } -void vewhicle_spiderbot_dinit() +float spiderbot_impulse(float _imp) { - 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)) + switch(_imp) { - remove(self); - return; - } - - - self.gun1 = spawn(); - self.gun2 = spawn(); + case 10: + case 15: + case 18: + self.vehicle.vehicle_weapon2mode += 1; + if(self.vehicle.vehicle_weapon2mode > SBRM_LAST) + self.vehicle.vehicle_weapon2mode = SBRM_FIRST; + + //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode))); + CSQCVehicleSetup(self, 0); + return TRUE; + case 12: + case 16: + case 19: + self.vehicle.vehicle_weapon2mode -= 1; + if(self.vehicle.vehicle_weapon2mode < SBRM_FIRST) + self.vehicle.vehicle_weapon2mode = SBRM_LAST; + + //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode))); + CSQCVehicleSetup(self, 0); + return TRUE; + + /* + case 17: // toss gun, could be used to exit? + break; + case 20: // Manual minigun reload? + break; + */ + } + return FALSE; +} - setmodel(self.gun1, "models/vehicles/spiderbot_barrels.dpm"); - setmodel(self.gun2, "models/vehicles/spiderbot_barrels.dpm"); +void spiderbot_spawn(float _f) +{ + 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; + } - setattachment(self.gun1, self.tur_head, "tag_hardpoint01"); - setattachment(self.gun2, self.tur_head, "tag_hardpoint02"); + 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.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; @@ -651,7 +846,6 @@ void spawnfunc_vehicle_spiderbot() precache_sound ( "vehicles/spiderbot_walk.wav"); precache_sound ( "vehicles/spiderbot_land.wav"); - vehicles_configcheck("vehicle_spiderbot.cfg", autocvar_g_vehicle_spiderbot_health); if(autocvar_g_vehicle_spiderbot_shield) self.vehicle_flags |= VHF_HASSHIELD; @@ -660,12 +854,26 @@ void spawnfunc_vehicle_spiderbot() if(autocvar_g_vehicle_spiderbot_health_regen) self.vehicle_flags |= VHF_HEALTHREGEN; - - self.think = vewhicle_spiderbot_dinit; - - if(g_assault) - self.nextthink = time + 0.5; - else - self.nextthink = time + (autocvar_g_vehicles_delayspawn ? autocvar_g_vehicle_spiderbot_respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter) : 0.5); + + if not (vehicle_initialize( + "Spiderbot", + "models/vehicles/spiderbot.dpm", + "models/vehicles/spiderbot_top.dpm", + "models/vehicles/spiderbot_cockpit.dpm", + "tag_head", "tag_hud", "", + HUD_SPIDERBOT, + SPIDERBOT_MIN, SPIDERBOT_MAX, + FALSE, + spiderbot_spawn, autocvar_g_vehicle_spiderbot_respawntime, + spiderbot_frame, + spiderbot_enter, spiderbot_exit, + spiderbot_die, spiderbot_think, + FALSE, + autocvar_g_vehicle_spiderbot_health, + autocvar_g_vehicle_spiderbot_shield)) + { + remove(self); + return; + } } #endif // SVQC diff --git a/qcsrc/server/vehicles/vehicles.qc b/qcsrc/server/vehicles/vehicles.qc index 20c74d3a17..16a339e09f 100644 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@ -2,7 +2,14 @@ float autocvar_g_vehicles_crush_dmg; float autocvar_g_vehicles_crush_force; float autocvar_g_vehicles_delayspawn; float autocvar_g_vehicles_delayspawn_jitter; -float autocvar_g_vehicles_allow_flagcarry; + +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(); @@ -39,6 +46,9 @@ float SendAuxiliaryXhair(entity to, float sf) void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id) { + if (clienttype(own) != CLIENTTYPE_REAL) + return; + entity axh; axh_id = bound(0, axh_id, MAX_AXH); @@ -64,7 +74,7 @@ void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id) // WriteByte(MSG_ONE, SVC_TEMPENTITY) uses reliable messagess, never use for thinsg that need continous updates. void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id) { - msg_entity = own; + msgexntity = own; WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_AUXILIARYXHAIR); @@ -84,18 +94,24 @@ void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id) // End AuxiliaryXhair /** - Notifies the client that he enterd a vehicle, and sends + Notifies the client that he enterd a vehicle, and sends realavent data. - + only sends vehicle_id atm (wich is a HUD_* constant, ex. HUD_SPIDERBOT) **/ void CSQCVehicleSetup(entity own, float vehicle_id) { + if (clienttype(own) != CLIENTTYPE_REAL) + return; + msg_entity = own; WriteByte(MSG_ONE, SVC_TEMPENTITY); WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP); - WriteByte(MSG_ONE, vehicle_id); + if(vehicle_id != 0) + WriteByte(MSG_ONE, vehicle_id); + else + WriteByte(MSG_ONE, 1 + own.vehicle.vehicle_weapon2mode + HUD_VEHICLE_LAST); } /** vehicles_locktarget @@ -115,6 +131,121 @@ void CSQCVehicleSetup(entity own, float vehicle_id) .float lock_strength; .float lock_time; .float lock_soundtime; +float DAMAGE_TARGETDRONE = 10; + +vector targetdrone_getnewspot() +{ + + vector spot; + float i; + for(i = 0; i < 100; ++i) + { + spot = self.origin + randomvec() * 1024; + tracebox(spot, self.mins, self.maxs, spot, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && trace_startsolid == 0 && trace_allsolid == 0) + return spot; + } + return self.origin; +} + +#if 0 +void targetdrone_think(); +void targetdrone_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); +void targetdrone_renwe() +{ + self.think = targetdrone_think; + self.nextthink = time + 0.1; + setorigin(self, targetdrone_getnewspot()); + self.health = 200; + self.takedamage = DAMAGE_TARGETDRONE; + self.event_damage = targetdrone_damage; + self.solid = SOLID_BBOX; + setmodel(self, "models/runematch/rune.mdl"); + self.effects = EF_LOWPRECISION; + self.scale = 10; + self.movetype = MOVETYPE_BOUNCEMISSILE; + setsize(self, '-100 -100 -100', '100 100 100'); + +} +void targetdrone_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + self.health -= damage; + if(self.health <= 0) + { + pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1); + + if(!self.cnt) + remove(self); + else + { + self.think = targetdrone_renwe; + self.nextthink = time + 1 + random() * 2; + self.solid = SOLID_NOT; + setmodel(self, ""); + } + } +} +entity targetdrone_getfear() +{ + entity fear; + float i; + + for(i = 64; i <= 1024; i += 64) + { + fear = findradius(self.origin, i); + while(fear) + { + if(fear.bot_dodge) + return fear; + + fear = fear.chain; + } + } + + return world; +} +void targetdrone_think() +{ + self.nextthink = time + 0.1; + + if(self.wp00) + if(self.wp00.deadflag != DEAD_NO) + self.wp00 = targetdrone_getfear(); + + if(!self.wp00) + self.wp00 = targetdrone_getfear(); + + vector newdir; + + if(self.wp00) + newdir = steerlib_push(self.wp00.origin) + randomvec() * 0.75; + else + newdir = randomvec() * 0.75; + + newdir = newdir * 0.5 + normalize(self.velocity) * 0.5; + + if(self.wp00) + self.velocity = normalize(newdir) * (500 + (1024 / min(vlen(self.wp00.origin - self.origin), 1024)) * 700); + else + self.velocity = normalize(newdir) * 750; + + tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 2, MOVE_NORMAL, self); + if(trace_fraction != 1.0) + self.velocity = self.velocity * -1; + + //normalize((normalize(self.velocity) * 0.5 + newdir * 0.5)) * 750; +} + +void targetdrone_spawn(vector _where, float _autorenew) +{ + entity drone = spawn(); + setorigin(drone, _where); + drone.think = targetdrone_renwe; + drone.nextthink = time + 0.1; + drone.cnt = _autorenew; +} +#endif + void vehicles_locktarget(float incr, float decr, float _lock_time) { if(self.lock_target && self.lock_target.deadflag != DEAD_NO) @@ -132,7 +263,7 @@ void vehicles_locktarget(float incr, float decr, float _lock_time) self.lock_soundtime = time + 0.5; play2(self.owner, "vehicles/locked.wav"); } - + return; } @@ -144,28 +275,30 @@ void vehicles_locktarget(float incr, float decr, float _lock_time) if(trace_ent.deadflag != DEAD_NO) trace_ent = world; - if not (trace_ent.vehicle_flags & VHF_ISVEHICLE || trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET) + if not (trace_ent.vehicle_flags & VHF_ISVEHICLE || + trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET || + trace_ent.takedamage == DAMAGE_TARGETDRONE) trace_ent = world; } if(self.lock_target == world && trace_ent != world) self.lock_target = trace_ent; - - if(self.lock_target && trace_ent == self.lock_target) - { + + if(self.lock_target && trace_ent == self.lock_target) + { if(self.lock_strength != 1 && self.lock_strength + incr >= 1) { play2(self.owner, "vehicles/lock.wav"); self.lock_soundtime = time + 0.8; - } + } else if (self.lock_strength != 1 && self.lock_soundtime < time) - { + { play2(self.owner, "vehicles/locking.wav"); self.lock_soundtime = time + 0.3; } - - } - + + } + // Have a locking target // Trace hit current target if(trace_ent == self.lock_target && trace_ent != world) @@ -186,8 +319,8 @@ void vehicles_locktarget(float incr, float decr, float _lock_time) } } -#define VEHICLE_UPDATE_PLAYER(fld,vhname) \ -self.owner.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 +#define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \ +ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 #define vehicles_sweap_collision(orig,vel,dt,acm,mult) \ traceline(orig, orig + vel * dt, MOVE_NORMAL, self); \ @@ -237,8 +370,8 @@ void vehicles_projectile_damage(entity inflictor, entity attacker, float damage, { // Ignore damage from oterh projectiles from my owner (dont mess up volly's) if(inflictor.owner == self.owner) - return; - + return; + self.health -= damage; self.velocity += force; if(self.health < 1) @@ -248,7 +381,6 @@ void vehicles_projectile_damage(entity inflictor, entity attacker, float damage, self.think = self.use; self.nextthink = time; } - } void vehicles_projectile_explode() @@ -274,7 +406,7 @@ entity vehicles_projectile(string _mzlfx, string _mzlsound, vector _org, vector _vel, float _dmg, float _radi, float _force, float _size, float _deahtype, float _projtype, float _health, - float _cull, float _clianim) + float _cull, float _clianim, entity _owner) { entity proj; @@ -296,7 +428,7 @@ entity vehicles_projectile(string _mzlfx, string _mzlsound, proj.touch = vehicles_projectile_explode; proj.use = vehicles_projectile_explode; proj.owner = self; - proj.realowner = self.owner; + proj.realowner = _owner; proj.think = SUB_Remove; proj.nextthink = time + 30; @@ -339,6 +471,7 @@ void vehicles_spawn() self.touch = vehicles_touch; self.event_damage = vehicles_damage; self.iscreature = TRUE; + self.teleportable = FALSE; // no teleporting for vehicles, too buggy self.damagedbycontents = TRUE; self.movetype = MOVETYPE_WALK; self.solid = SOLID_SLIDEBOX; @@ -359,9 +492,12 @@ void vehicles_spawn() setorigin(self, self.pos1 + '0 0 0'); // Show it pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1); - + + if(self.vehicle_controller) + self.team = self.vehicle_controller.team; + vehicles_reset_colors(); - self.vehicle_spawn(); + self.vehicle_spawn(VHSF_NORMAL); } // Better way of determening whats crushable needed! (fl_crushable?) @@ -377,21 +513,21 @@ float vehicles_crushable(entity e) } void vehilces_impact(float _minspeed, float _speedfac, float _maxpain) -{ +{ if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) return; - + if(self.play_time < time) - { + { float wc = vlen(self.velocity - self.oldvelocity); //dprint("oldvel: ", vtos(self.oldvelocity), "\n"); //dprint("vel: ", vtos(self.velocity), "\n"); if(_minspeed < wc) { - float take = take = min(_speedfac * wc, _maxpain); + float take = min(_speedfac * wc, _maxpain); Damage (self, world, world, take, DEATH_FALL, self.origin, '0 0 0'); self.play_time = time + 0.25; - + //dprint("wc: ", ftos(wc), "\n"); //dprint("take: ", ftos(take), "\n"); } @@ -409,14 +545,14 @@ void vehicles_touch() { if(vlen(self.velocity) != 0) Damage(other, self, self.owner, autocvar_g_vehicles_crush_dmg, DEATH_VHCRUSH, '0 0 0', normalize(other.origin - self.origin) * autocvar_g_vehicles_crush_force); - + return; // Dont do selfdamage when hitting "soft targets". } - + if(self.play_time < time) if(self.vehicle_impact) self.vehicle_impact(); - + return; } @@ -429,18 +565,18 @@ void vehicles_touch() if(other.vehicle != world) return; - // Remove this when bots know how to use vehicles. - if (clienttype(other) != CLIENTTYPE_REAL) - return; - vehicles_enter(); } - +var float autocvar_g_vehicles_allow_bots = 0; void vehicles_enter() { // Remove this when bots know how to use vehicles - if (clienttype(other) != CLIENTTYPE_REAL) - return; + + if (clienttype(other) == CLIENTTYPE_BOT) + if (autocvar_g_vehicles_allow_bots) + dprint("Bot enters vehicle\n"); // This is where we need to disconnect (some, all?) normal bot AI and hand over to vehicle's _aiframe() + else + return; if(self.phase > time) return; @@ -449,7 +585,7 @@ void vehicles_enter() if(self.team) if(self.team != other.team) return; - + RemoveGrapplingHook(other); self.vehicle_ammo1 = 0; @@ -497,41 +633,38 @@ void vehicles_enter() self.team = self.owner.team; self.flags -= FL_NOTARGET; - - msg_entity = other; - WriteByte (MSG_ONE, SVC_SETVIEWPORT); - WriteEntity(MSG_ONE, self.vehicle_viewport); - - WriteByte (MSG_ONE, SVC_SETVIEWANGLES); - if(self.tur_head) - { - WriteAngle(MSG_ONE, self.tur_head.angles_x + self.angles_x); // tilt - WriteAngle(MSG_ONE, self.tur_head.angles_y + self.angles_y); // yaw - WriteAngle(MSG_ONE, 0); // roll - } - else + + if (clienttype(other) == CLIENTTYPE_REAL) { - WriteAngle(MSG_ONE, self.angles_x * -1); // tilt - WriteAngle(MSG_ONE, self.angles_y); // yaw - WriteAngle(MSG_ONE, 0); // roll + msg_entity = other; + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity(MSG_ONE, self.vehicle_viewport); + + WriteByte (MSG_ONE, SVC_SETVIEWANGLES); + if(self.tur_head) + { + WriteAngle(MSG_ONE, self.tur_head.angles_x + self.angles_x); // tilt + WriteAngle(MSG_ONE, self.tur_head.angles_y + self.angles_y); // yaw + WriteAngle(MSG_ONE, 0); // roll + } + else + { + WriteAngle(MSG_ONE, self.angles_x * -1); // tilt + WriteAngle(MSG_ONE, self.angles_y); // yaw + WriteAngle(MSG_ONE, 0); // roll + } } vehicles_clearrturn(); CSQCVehicleSetup(self.owner, self.hud); - if(other.flagcarried) - { - if(!autocvar_g_vehicles_allow_flagcarry) - DropFlag(other.flagcarried, world, world); - else - { - other.flagcarried.scale = 1; - setattachment(other.flagcarried, self, ""); - setorigin(other, '0 0 96'); - } - } - + vh_player = other; + vh_vehicle = self; + MUTATOR_CALLHOOK(VehicleEnter); + other = vh_player; + self = vh_vehicle; + self.vehicle_enter(); antilag_clear(other); } @@ -545,17 +678,17 @@ vector vehicles_findgoodexit(vector prefer_spot) { //vector exitspot; float mysize; - + tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, prefer_spot, MOVE_NORMAL, self.owner); 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); for(i = 0; i < 100; ++i) - { + { v = randomvec(); v_z = 0; v = v2 + normalize(v) * mysize; @@ -563,13 +696,13 @@ vector vehicles_findgoodexit(vector prefer_spot) if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return v; } - + /* exitspot = (self.origin + '0 0 48') + v_forward * mysize; tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return exitspot; - + exitspot = (self.origin + '0 0 48') - v_forward * mysize; tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) @@ -579,13 +712,13 @@ vector vehicles_findgoodexit(vector prefer_spot) tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return exitspot; - + exitspot = (self.origin + '0 0 48') - v_right * mysize; tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return exitspot; */ - + return self.origin; } @@ -593,82 +726,111 @@ vector vehicles_findgoodexit(vector prefer_spot) Standarrd vehicle release fucntion. custom code goes in self.vehicle_exit **/ +float vehicles_exit_running; void vehicles_exit(float eject) -{ - entity oldself; +{ + entity _vehicle; + entity _player; + entity _oldself = self; + + if(vehicles_exit_running) + { + dprint("^1vehicles_exit allready running! this is not good..\n"); + return; + } + + vehicles_exit_running = TRUE; if(self.flags & FL_CLIENT) { - oldself = self; - self = self.vehicle; + _vehicle = self.vehicle; + + if (_vehicle.vehicle_flags & VHF_PLAYERSLOT) + { + _vehicle.vehicle_exit(eject); + self = _oldself; + vehicles_exit_running = FALSE; + return; + } } + else + _vehicle = self; - self.flags |= FL_NOTARGET; + _player = _vehicle.owner; + + self = _vehicle; - if (self.owner) + if (_player) { - 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; + 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); + } - CSQCVehicleSetup(self.owner, HUD_NORMAL); + 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); } - - 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; - + _vehicle.flags |= FL_NOTARGET; + + if(_vehicle.deadflag == DEAD_NO) + _vehicle.avelocity = '0 0 0'; + + _vehicle.tur_head.nodrawtoclient = world; + if(!teamplay) - self.team = 0; + _vehicle.team = 0; else - self.team = self.tur_head.team; + + vh_player = _player; + vh_vehicle = _vehicle; + MUTATOR_CALLHOOK(VehicleExit); + _player = vh_player; + _vehicle = vh_vehicle; + + _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; - if(self.owner.flagcarried) - { - self.owner.flagcarried.scale = 0.6; - setattachment(self.owner.flagcarried, self.owner, ""); - setorigin(self.owner.flagcarried, FLAG_CARRY_POS); - } + _vehicle.vehicle_exit(eject); - sound (self, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTN_NORM); - self.vehicle_exit(eject); - self.owner = world; - vehicles_reset_colors(); + vehicles_setreturn(); + vehicles_reset_colors(); + _vehicle.owner = world; + self = _oldself; - if(oldself) - self = oldself; + vehicles_exit_running = FALSE; } -void vehicles_regen(.float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time) +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); + self.regen_field = min(self.regen_field + regen * delta_time, field_max); if(self.owner) @@ -683,6 +845,7 @@ void shieldhit_think() { //setmodel(self, ""); self.alpha = -1; + self.effects |= EF_NODRAW; } else { @@ -691,31 +854,48 @@ void shieldhit_think() } void vehicles_painframe() -{ +{ if(self.owner.vehicle_health <= 50) if(self.pain_frame < time) - { - float _ftmp; + { + float _ftmp; _ftmp = self.owner.vehicle_health / 50; self.pain_frame = time + 0.1 + (random() * 0.5 * _ftmp); pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); - + if(self.vehicle_flags & VHF_DMGSHAKE) self.velocity += randomvec() * 30; - + if(self.vehicle_flags & VHF_DMGROLL) if(self.vehicle_flags & VHF_DMGHEADROLL) self.tur_head.angles += randomvec(); else self.angles += randomvec(); - - } + + } } void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { self.dmg_time = time; + + if(DEATH_ISWEAPON(deathtype, WEP_NEX)) + damage *= autocvar_g_vehicles_nex_damagerate; + + if(DEATH_ISWEAPON(deathtype, WEP_UZI)) + damage *= autocvar_g_vehicles_uzi_damagerate; + + if(DEATH_ISWEAPON(deathtype, WEP_RIFLE)) + damage *= autocvar_g_vehicles_rifle_damagerate; + + 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; + if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0)) { if (wasfreed(self.vehicle_shieldent) || self.vehicle_shieldent == world) @@ -734,6 +914,7 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat self.vehicle_shieldent.alpha = 0.45; self.vehicle_shieldent.angles = vectoangles(normalize(hitloc - (self.origin + self.vehicle_shieldent.origin))) - self.angles; self.vehicle_shieldent.nextthink = time; + self.vehicle_shieldent.effects &~= EF_NODRAW; self.vehicle_shield -= damage; @@ -743,7 +924,7 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat self.vehicle_shieldent.colormod = '2 0 0'; self.vehicle_shield = 0; self.vehicle_shieldent.alpha = 0.75; - + if(sound_allowed(MSG_BROADCAST, attacker)) spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM); // FIXME: PLACEHOLDER } @@ -759,8 +940,11 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat if(sound_allowed(MSG_BROADCAST, attacker)) spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM); // FIXME: PLACEHOLDER } - - self.velocity += force; // * (vlen(force) / self.mass); + + if(self.damageforcescale < 1 && self.damageforcescale > 0) + self.velocity += force * self.damageforcescale; + else + self.velocity += force; if(self.vehicle_health <= 0) { @@ -770,6 +954,9 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat else vehicles_exit(VHEF_RELESE); + + antilag_clear(self); + self.vehicle_die(); vehicles_setreturn(); } @@ -782,15 +969,15 @@ void vehicles_clearrturn() ret = findchain(classname, "vehicle_return"); while(ret) { - if(ret.enemy == self) + if(ret.wp00 == self) { ret.classname = ""; ret.think = SUB_Remove; - ret.nextthink = time + 0.1; - + ret.nextthink = time + 0.1; + if(ret.waypointsprite_attached) WaypointSprite_Kill(ret.waypointsprite_attached); - + return; } ret = ret.chain; @@ -799,14 +986,14 @@ void vehicles_clearrturn() void vehicles_return() { - pointparticles(particleeffectnum("teleport"), self.enemy.origin + '0 0 64', '0 0 0', 1); + pointparticles(particleeffectnum("teleport"), self.wp00.origin + '0 0 64', '0 0 0', 1); - self.enemy.think = vehicles_spawn; - self.enemy.nextthink = time; + self.wp00.think = vehicles_spawn; + self.wp00.nextthink = time; if(self.waypointsprite_attached) WaypointSprite_Kill(self.waypointsprite_attached); - + remove(self); } @@ -814,50 +1001,50 @@ void vehicles_showwp_goaway() { if(self.waypointsprite_attached) WaypointSprite_Kill(self.waypointsprite_attached); - + remove(self); - + } void vehicles_showwp() { entity oldself; vector rgb; - + if(self.cnt) - { + { self.think = vehicles_return; self.nextthink = self.cnt; - } + } else { self.think = vehicles_return; self.nextthink = time +1; - + oldself = self; self = spawn(); setmodel(self, "null"); - self.team = oldself.enemy.team; - self.enemy = oldself.enemy; - setorigin(self, oldself.enemy.pos1); - + self.team = oldself.wp00.team; + self.wp00 = oldself.wp00; + setorigin(self, oldself.wp00.pos1); + self.nextthink = time + 5; self.think = vehicles_showwp_goaway; } - + if(teamplay && self.team) rgb = TeamColor(self.team); else rgb = '1 1 1'; WaypointSprite_Spawn("vehicle", 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, TRUE, RADARICON_POWERUP, rgb); if(self.waypointsprite_attached) - { - WaypointSprite_UpdateRule(self.waypointsprite_attached, self.enemy.team, SPRITERULE_DEFAULT); + { + WaypointSprite_UpdateRule(self.waypointsprite_attached, self.wp00.team, SPRITERULE_DEFAULT); if(oldself == world) - WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, self.nextthink); + WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, self.nextthink); WaypointSprite_Ping(self.waypointsprite_attached); - } - + } + if(oldself != world) self = oldself; } @@ -865,34 +1052,28 @@ void vehicles_showwp() void vehicles_setreturn() { entity ret; - + vehicles_clearrturn(); ret = spawn(); ret.classname = "vehicle_return"; - ret.enemy = self; + ret.wp00 = self; ret.team = self.team; ret.think = vehicles_showwp; - + if(self.deadflag != DEAD_NO) { ret.cnt = time + self.vehicle_respawntime; - ret.nextthink = min(time + self.vehicle_respawntime, time + self.vehicle_respawntime - 5); - } + ret.nextthink = min(time + self.vehicle_respawntime, time + self.vehicle_respawntime - 5); + } else { - ret.nextthink = min(time + self.vehicle_respawntime, time + self.vehicle_respawntime - 1); + ret.nextthink = min(time + self.vehicle_respawntime, time + self.vehicle_respawntime - 1); } - + setmodel(ret, "null"); setorigin(ret, self.pos1 + '0 0 96'); - -} -void vehicles_configcheck(string configname, float check_cvar) -{ - if(check_cvar == 0) - localcmd(strcat("exec ", configname, "\n")); } void vehicles_reset_colors() @@ -940,6 +1121,57 @@ void vehicles_reset_colors() self.effects = _effects; } +void vehicle_use() +{ + dprint("vehicle ",self.netname, " used by ", activator.classname, "\n"); + + self.tur_head.team = activator.team; + + if(self.tur_head.team == 0) + self.active = ACTIVE_NOT; + else + self.active = ACTIVE_ACTIVE; + + if(self.active == ACTIVE_ACTIVE && self.deadflag == DEAD_NO) + { + dprint("^3Eat shit yall!\n"); + vehicles_setreturn(); + vehicles_reset_colors(); + } + else if(self.active == ACTIVE_NOT && self.deadflag != DEAD_NO) + { + + } +} + +float vehicle_addplayerslot( entity _owner, + entity _slot, + float _hud, + string _hud_model, + float() _framefunc, + void(float) _exitfunc) +{ + if not (_owner.vehicle_flags & VHF_MULTISLOT) + _owner.vehicle_flags |= VHF_MULTISLOT; + + _slot.PlayerPhysplug = _framefunc; + _slot.vehicle_exit = _exitfunc; + _slot.hud = _hud; + _slot.vehicle_flags = VHF_PLAYERSLOT; + _slot.vehicle_viewport = spawn(); + _slot.vehicle_hudmodel = spawn(); + _slot.vehicle_hudmodel.viewmodelforclient = _slot; + _slot.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT); + + setmodel(_slot.vehicle_hudmodel, _hud_model); + setmodel(_slot.vehicle_viewport, "null"); + + setattachment(_slot.vehicle_hudmodel, _slot, ""); + setattachment(_slot.vehicle_viewport, _slot.vehicle_hudmodel, ""); + + return TRUE; +} + float vehicle_initialize(string net_name, string bodymodel, string topmodel, @@ -951,24 +1183,55 @@ float vehicle_initialize(string net_name, vector min_s, vector max_s, float nodrop, - void() spawnproc, + void(float _spawnflag) spawnproc, float _respawntime, float() physproc, void() enterproc, void(float extflag) exitfunc, void() dieproc, void() thinkproc, - float use_csqc) + float use_csqc, + 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 == "") @@ -984,9 +1247,9 @@ float vehicle_initialize(string net_name, if(self.team && !teamplay) self.team = 0; - + self.vehicle_flags |= VHF_ISVEHICLE; - + setmodel(self, bodymodel); self.vehicle_viewport = spawn(); @@ -996,19 +1259,27 @@ float vehicle_initialize(string net_name, self.takedamage = DAMAGE_AIM; self.bot_attack = TRUE; self.iscreature = TRUE; + self.teleportable = FALSE; // no teleporting for vehicles, too buggy self.damagedbycontents = TRUE; self.hud = vhud; - + self.tur_health = _max_health; + self.tur_head.tur_health = _max_shield; self.vehicle_die = dieproc; self.vehicle_exit = exitfunc; self.vehicle_enter = enterproc; self.PlayerPhysplug = physproc; self.event_damage = vehicles_damage; self.touch = vehicles_touch; - self.think = vehicles_spawn; - self.nextthink = time; + self.think = vehicles_spawn; + 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; @@ -1019,7 +1290,6 @@ float vehicle_initialize(string net_name, setmodel(self.vehicle_hudmodel, hudmodel); setmodel(self.vehicle_viewport, "null"); - if(topmodel != "") { setmodel(self.tur_head, topmodel); @@ -1041,19 +1311,98 @@ float vehicle_initialize(string net_name, tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self); setorigin(self, trace_endpos); } - + self.pos1 = self.origin; self.pos2 = self.angles; self.tur_head.team = self.team; - + return TRUE; } -void bugmenot() +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, vtag; + float ftmp; + 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 * 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); + return vtag; +} + +void vehicles_gib_explode() { - 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; + 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 vehicles_gib_think() +{ + 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; +} +*/ diff --git a/qcsrc/server/vehicles/vehicles.qh b/qcsrc/server/vehicles/vehicles.qh index 79fc9cbf9a..549dfeab7f 100644 --- a/qcsrc/server/vehicles/vehicles.qh +++ b/qcsrc/server/vehicles/vehicles.qh @@ -4,5 +4,7 @@ #include "racer.qc" #include "spiderbot.qc" #include "raptor.qc" -//#include "bumblebee.qc" +#ifndef VEHICLES_NO_UNSTABLE +#include "bumblebee.qc" +#endif #endif diff --git a/qcsrc/server/vehicles/vehicles_def.qh b/qcsrc/server/vehicles/vehicles_def.qh index dcb1854f36..834e651678 100644 --- a/qcsrc/server/vehicles/vehicles_def.qh +++ b/qcsrc/server/vehicles/vehicles_def.qh @@ -2,8 +2,6 @@ #define VEHICLES_ENABLED #ifdef VEHICLES_ENABLED -//#message "with tZork vehicles (experimental)" - .float vehicle_flags; float VHF_ISVEHICLE = 2; /// Indicates vehicle float VHF_HASSHIELD = 4; /// Vehicle has shileding @@ -14,34 +12,40 @@ float VHF_DEATHEJECT = 64; /// Vehicle ejects pilot upon fatal damage float VHF_MOVE_GROUND = 128; /// Vehicle moves on gound float VHF_MOVE_HOVER = 256; /// Vehicle hover close to gound float VHF_MOVE_FLY = 512; /// Vehicle is airborn -float VHF_DMGSHAKE = 1024; -float VHF_DMGROLL = 2048; -float VHF_DMGHEADROLL = 4096; +float VHF_DMGSHAKE = 1024; /// Add random velocity each frame if health < 50% +float VHF_DMGROLL = 2048; /// Add random angles each frame if health < 50% +float VHF_DMGHEADROLL = 4096; /// Add random head angles each frame if health < 50% +float VHF_MULTISLOT = 8192; /// Vehicle has multiple player slots +float VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a multi-person vehicle .entity gun1; .entity gun2; +.entity gun3; +.entity vehicle_shieldent; /// Entity to disply the shild effect on damage +.entity vehicle; +.entity vehicle_viewport; +.entity vehicle_hudmodel; +.entity vehicle_controller; + +.entity gunner1; +.entity gunner2; .float vehicle_health; /// If self is player this is 0..100 indicating precentage of health left on vehicle. If self is vehile, this is the real health value. .float vehicle_energy; /// If self is player this is 0..100 indicating precentage of energy left on vehicle. If self is vehile, this is the real energy value. .float vehicle_shield; /// If self is player this is 0..100 indicating precentage of shield left on vehicle. If self is vehile, this is the real shield value. -.entity vehicle_shieldent; /// Entity to disply the shild effect on damage .float vehicle_ammo1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo1 value. .float vehicle_reload1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload1 value. .float vehicle_ammo2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo2 value. .float vehicle_reload2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload2 value. -.entity vehicle; -.entity vehicle_viewport; -.entity vehicle_hudmodel; - .float sound_nexttime; #define VOL_VEHICLEENGINE 1 .float hud; .float dmg_time; .float vehicle_respawntime; -.void() vehicle_spawn; +//.void() vehicle_spawn; void vehicles_exit(float eject); var .void(float exit_flags) vehicle_exit; @@ -55,7 +59,14 @@ float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80 var .void() vehicle_enter; /// Vehicles custom funciton to be executed when owner exit it var .void() vehicle_die; /// Vehicles custom function to be executed when vehile die -var .void() vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns +#define VHSF_NORMAL 0 +#define VHSF_FACTORY 2 +var .void(float _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns +const var .float(float _imp) vehicles_impusle_null; +var .float(float _imp) vehicles_impusle; +.float vehicle_weapon2mode = volly_counter; + +//§ var .void() vehicle_factory() #ifdef VEHICLES_USE_ODE void(entity e, float physics_enabled) physics_enable = #540; // enable or disable physics on object diff --git a/qcsrc/server/w_common.qc b/qcsrc/server/w_common.qc index fab3b41b22..3780fc7138 100644 --- a/qcsrc/server/w_common.qc +++ b/qcsrc/server/w_common.qc @@ -6,7 +6,7 @@ void W_GiveWeapon (entity e, float wep, string name) if (!wep) return; - e.weapons = e.weapons | W_WeaponBit(wep); + WEPSET_OR_EW(e, wep); oldself = self; self = e; @@ -38,6 +38,8 @@ void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, f entity pseudoprojectile; float f, ffs; + pseudoprojectile = world; + railgun_start = start; railgun_end = end; @@ -241,31 +243,58 @@ void W_BallisticBullet_LeaveSolid_think() UpdateCSQCProjectile(self); } -float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant) +float W_BallisticBullet_LeaveSolid(float eff) { // move the entity along its velocity until it's out of solid, then let it resume - + vector vel = self.velocity; float dt, dst, velfactor, v0, vs; float maxdist; float E0_m, Es_m; + float constant = self.dmg_radius * (other.ballistics_density ? other.ballistics_density : 1); // outside the world? forget it if(self.origin_x > world.maxs_x || self.origin_y > world.maxs_y || self.origin_z > world.maxs_z || self.origin_x < world.mins_x || self.origin_y < world.mins_y || self.origin_z < world.mins_z) return 0; + // special case for zero density and zero bullet constant: + + if(self.dmg_radius == 0) + { + if(other.ballistics_density < 0) + constant = 0; // infinite travel distance + else + return 0; // no penetration + } + else + { + if(other.ballistics_density < 0) + constant = 0; // infinite travel distance + else if(other.ballistics_density == 0) + constant = self.dmg_radius; + else + constant = self.dmg_radius * other.ballistics_density; + } + // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass v0 = vlen(vel); E0_m = 0.5 * v0 * v0; - maxdist = E0_m / constant; - // maxdist = 0.5 * v0 * v0 / constant - // dprint("max dist = ", ftos(maxdist), "\n"); - if(maxdist <= autocvar_g_ballistics_mindistance) - return 0; + if(constant) + { + maxdist = E0_m / constant; + // maxdist = 0.5 * v0 * v0 / constant + // dprint("max dist = ", ftos(maxdist), "\n"); - traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self); + if(maxdist <= autocvar_g_ballistics_mindistance) + return 0; + } + else + { + maxdist = vlen(other.maxs - other.mins) + 1; // any distance, as long as we leave the entity + } + traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self, TRUE); if(trace_fraction == 1) // 1: we never got out of solid return 0; @@ -298,12 +327,19 @@ float W_BallisticBullet_LeaveSolid(entity e, vector vel, float constant) self.flags |= FL_ONGROUND; // prevent moving self.W_BallisticBullet_LeaveSolid_velocity = vel; + if(eff >= 0) + if(vlen(trace_endpos - self.origin) > 4) + { + endzcurveparticles(); + trailparticles(self, eff, self.origin, trace_endpos); + } + return 1; } void W_BallisticBullet_Touch (void) { - float density; + //float density; if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this! return; @@ -311,6 +347,12 @@ void W_BallisticBullet_Touch (void) PROJECTILE_TOUCH; W_BallisticBullet_Hit (); + if(self.dmg_radius < 0) // these NEVER penetrate solid + { + remove(self); + return; + } + // if we hit "weapclip", bail out // // rationale of this check: @@ -331,12 +373,8 @@ void W_BallisticBullet_Touch (void) return; } - density = other.ballistics_density; - if(density == 0) - density = 1; - // go through solid! - if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density)) + if(!W_BallisticBullet_LeaveSolid(-1)) { remove(self); return; @@ -362,7 +400,7 @@ void fireBallisticBullet_trace_callback(vector start, vector hit, vector end) void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant) { - float lag, dt, savetime, density; + float lag, dt, savetime; //, density; entity pl, oldself; float antilagging; @@ -384,7 +422,12 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed); W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging); proj.angles = vectoangles(proj.velocity); - proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant; + if(bulletconstant > 0) + proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant; + else if(bulletconstant == 0) + proj.dmg_radius = 0; + else + proj.dmg_radius = -1; // so: bulletconstant = bullet mass / area of bullet circle setorigin(proj, start); proj.flags = FL_PROJECTILE; @@ -468,6 +511,9 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f W_BallisticBullet_Hit(); } + if(proj.dmg_radius < 0) // these NEVER penetrate solid + break; + // if we hit "weapclip", bail out // // rationale of this check: @@ -485,15 +531,13 @@ void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, f if not(trace_dphitcontents & DPCONTENTS_OPAQUE) break; - density = other.ballistics_density; - if(density == 0) - density = 1; - // go through solid! - if(!W_BallisticBullet_LeaveSolid(self, self.velocity, self.dmg_radius * density)) + if(!W_BallisticBullet_LeaveSolid((other && (other.solid != SOLID_BSP)) ? eff : -1)) break; W_BallisticBullet_LeaveSolid_think(); + + self.projectiledeathtype |= HITTYPE_BOUNCE; } frametime = savetime; self = oldself; @@ -585,9 +629,13 @@ void W_PrepareExplosionByDamage(entity attacker, void() explode) { self.takedamage = DAMAGE_NO; self.event_damage = SUB_Null; - self.owner = attacker; - self.realowner = attacker; - + + if((attacker.flags & FL_CLIENT) && !autocvar_g_projectiles_keep_owner) + { + self.owner = attacker; + self.realowner = attacker; + } + // do not explode NOW but in the NEXT FRAME! // because recursive calls to RadiusDamage are not allowed self.nextthink = time; diff --git a/qcsrc/server/w_crylink.qc b/qcsrc/server/w_crylink.qc index 0b3118bb60..bb44e827e7 100644 --- a/qcsrc/server/w_crylink.qc +++ b/qcsrc/server/w_crylink.qc @@ -133,6 +133,7 @@ vector W_Crylink_LinkJoin(entity e, float jspeed, float jtime) p.velocity = WarpZone_RefSys_TransformVelocity(e, p, avg_velocity); UpdateCSQCProjectile(p); } + targ_origin = avg_origin + 1000000000 * normalize(avg_velocity); // HUUUUUUGE } else { @@ -224,6 +225,28 @@ void W_Crylink_LinkJoinEffect_Think() remove(self); } +float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad) +{ + entity head = WarpZone_FindRadius((projectile.origin + (projectile.mins + projectile.maxs) * 0.5), rad + MAX_DAMAGEEXTRARADIUS, FALSE); + float hit_friendly; + float hit_enemy; + + while(head) + { + if((head.takedamage != DAMAGE_NO) && (head.deadflag == DEAD_NO)) + { + if(IsDifferentTeam(head, projectile.realowner)) + ++hit_enemy; + else + ++hit_friendly; + } + + head = head.chain; + } + + return (hit_enemy ? FALSE : hit_friendly); +} + // NO bounce protection, as bounces are limited! void W_Crylink_Touch (void) { @@ -241,7 +264,10 @@ void W_Crylink_Touch (void) f = autocvar_g_balance_crylink_primary_bouncedamagefactor; if(a) f *= a; - if (RadiusDamage (self, self.realowner, autocvar_g_balance_crylink_primary_damage * f, autocvar_g_balance_crylink_primary_edgedamage * f, autocvar_g_balance_crylink_primary_radius, world, autocvar_g_balance_crylink_primary_force * f, self.projectiledeathtype, other) && autocvar_g_balance_crylink_primary_linkexplode) + + float totaldamage = RadiusDamage(self, self.realowner, autocvar_g_balance_crylink_primary_damage * f, autocvar_g_balance_crylink_primary_edgedamage * f, autocvar_g_balance_crylink_primary_radius, world, autocvar_g_balance_crylink_primary_force * f, self.projectiledeathtype, other); + + if(totaldamage && ((autocvar_g_balance_crylink_primary_linkexplode == 2) || ((autocvar_g_balance_crylink_primary_linkexplode == 1) && !W_Crylink_Touch_WouldHitFriendly(self, autocvar_g_balance_crylink_primary_radius)))) { if(self == self.realowner.crylink_lastgroup) self.realowner.crylink_lastgroup = world; @@ -282,7 +308,10 @@ void W_Crylink_Touch2 (void) f = autocvar_g_balance_crylink_secondary_bouncedamagefactor; if(a) f *= a; - if (RadiusDamage (self, self.realowner, autocvar_g_balance_crylink_secondary_damage * f, autocvar_g_balance_crylink_secondary_edgedamage * f, autocvar_g_balance_crylink_secondary_radius, world, autocvar_g_balance_crylink_secondary_force * f, self.projectiledeathtype, other) && autocvar_g_balance_crylink_secondary_linkexplode) + + float totaldamage = RadiusDamage(self, self.realowner, autocvar_g_balance_crylink_secondary_damage * f, autocvar_g_balance_crylink_secondary_edgedamage * f, autocvar_g_balance_crylink_secondary_radius, world, autocvar_g_balance_crylink_secondary_force * f, self.projectiledeathtype, other); + + if(totaldamage && ((autocvar_g_balance_crylink_secondary_linkexplode == 2) || ((autocvar_g_balance_crylink_secondary_linkexplode == 1) && !W_Crylink_Touch_WouldHitFriendly(self, autocvar_g_balance_crylink_secondary_radius)))) { if(self == self.realowner.crylink_lastgroup) self.realowner.crylink_lastgroup = world; @@ -335,8 +364,8 @@ void W_Crylink_Attack (void) shots = autocvar_g_balance_crylink_primary_shots; pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots); - proj = world; - while (counter < shots) + proj = prevproj = firstproj = world; + for(counter = 0; counter < shots; ++counter) { proj = spawn (); proj.reset = W_Crylink_Reset; @@ -408,12 +437,11 @@ void W_Crylink_Attack (void) //proj.glow_size = 20; proj.flags = FL_PROJECTILE; - + proj.missile_flags = MIF_SPLASH; + CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE); other = proj; MUTATOR_CALLHOOK(EditProjectile); - - counter = counter + 1; } if(autocvar_g_balance_crylink_primary_joinspread != 0 || autocvar_g_balance_crylink_primary_jointime != 0) { @@ -427,6 +455,8 @@ void W_Crylink_Attack2 (void) { float counter, shots; entity proj, prevproj, firstproj; + vector s; + vector forward, right, up; float maxdmg; W_DecreaseAmmo(ammo_cells, autocvar_g_balance_crylink_secondary_ammo, autocvar_g_balance_crylink_reload_ammo); @@ -437,11 +467,14 @@ void W_Crylink_Attack2 (void) maxdmg += autocvar_g_balance_crylink_secondary_joinexplode_damage; W_SetupShot (self, FALSE, 2, "weapons/crylink_fire2.wav", CH_WEAPON_A, maxdmg); + forward = v_forward; + right = v_right; + up = v_up; shots = autocvar_g_balance_crylink_secondary_shots; pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots); - proj = world; - while (counter < shots) + proj = prevproj = firstproj = world; + for(counter = 0; counter < shots; ++counter) { proj = spawn (); proj.reset = W_Crylink_Reset; @@ -477,7 +510,26 @@ void W_Crylink_Attack2 (void) setorigin (proj, w_shotorg); setsize(proj, '0 0 0', '0 0 0'); - W_SetupProjectileVelocityEx(proj, (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor), v_up, autocvar_g_balance_crylink_secondary_speed, 0, 0, 0, FALSE); + if(autocvar_g_balance_crylink_secondary_spreadtype == 1) + { + s = '0 0 0'; + if (counter == 0) + s = '0 0 0'; + else + { + makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); + s_y = v_forward_x; + s_z = v_forward_y; + } + s = s * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor; + s = w_shotdir + right * s_y + up * s_z; + } + else + { + s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor); + } + + W_SetupProjectileVelocityEx(proj, s, v_up, autocvar_g_balance_crylink_secondary_speed, 0, 0, 0, FALSE); proj.touch = W_Crylink_Touch2; proj.think = W_Crylink_Fadethink; if(counter == (shots - 1) / 2) @@ -501,12 +553,11 @@ void W_Crylink_Attack2 (void) //proj.glow_size = 20; proj.flags = FL_PROJECTILE; - + proj.missile_flags = MIF_SPLASH; + CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE); other = proj; MUTATOR_CALLHOOK(EditProjectile); - - counter = counter + 1; } if(autocvar_g_balance_crylink_secondary_joinspread != 0 || autocvar_g_balance_crylink_secondary_jointime != 0) { diff --git a/qcsrc/server/w_electro.qc b/qcsrc/server/w_electro.qc index 9b1b96e565..7a91cbd89c 100644 --- a/qcsrc/server/w_electro.qc +++ b/qcsrc/server/w_electro.qc @@ -140,6 +140,7 @@ void W_Electro_Attack() proj.touch = W_Plasma_TouchExplode; setsize(proj, '0 0 -3', '0 0 -3'); proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH; CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); @@ -185,6 +186,7 @@ void W_Electro_Attack2() proj.bouncefactor = autocvar_g_balance_electro_secondary_bouncefactor; proj.bouncestop = autocvar_g_balance_electro_secondary_bouncestop; + proj.missile_flags = MIF_SPLASH | MIF_ARC; #if 0 entity p2; diff --git a/qcsrc/server/w_fireball.qc b/qcsrc/server/w_fireball.qc index 1c950aca4a..67547e3583 100644 --- a/qcsrc/server/w_fireball.qc +++ b/qcsrc/server/w_fireball.qc @@ -159,7 +159,8 @@ void W_Fireball_Attack1() proj.touch = W_Fireball_TouchExplode; setsize(proj, '-16 -16 -16', '16 16 16'); proj.flags = FL_PROJECTILE; - + proj.missile_flags = MIF_SPLASH | MIF_PROXY; + CSQCProjectile(proj, TRUE, PROJECTILE_FIREBALL, TRUE); other = proj; MUTATOR_CALLHOOK(EditProjectile); @@ -289,7 +290,8 @@ void W_Fireball_Attack2() proj.angles = vectoangles(proj.velocity); proj.flags = FL_PROJECTILE; - + proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; + CSQCProjectile(proj, TRUE, PROJECTILE_FIREMINE, TRUE); other = proj; MUTATOR_CALLHOOK(EditProjectile); diff --git a/qcsrc/server/w_grenadelauncher.qc b/qcsrc/server/w_grenadelauncher.qc index 352ce883bd..5f6c0346f5 100644 --- a/qcsrc/server/w_grenadelauncher.qc +++ b/qcsrc/server/w_grenadelauncher.qc @@ -200,6 +200,7 @@ void W_Grenade_Attack (void) gren.damageforcescale = autocvar_g_balance_grenadelauncher_primary_damageforcescale; gren.event_damage = W_Grenade_Damage; gren.damagedbycontents = TRUE; + gren.missile_flags = MIF_SPLASH | MIF_ARC; W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_primary); gren.angles = vectoangles (gren.velocity); @@ -247,6 +248,7 @@ void W_Grenade_Attack2 (void) gren.damageforcescale = autocvar_g_balance_grenadelauncher_secondary_damageforcescale; gren.event_damage = W_Grenade_Damage; gren.damagedbycontents = TRUE; + gren.missile_flags = MIF_SPLASH | MIF_ARC; W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_secondary); gren.angles = vectoangles (gren.velocity); diff --git a/qcsrc/server/w_hagar.qc b/qcsrc/server/w_hagar.qc index 76103f347e..9a4b1ef449 100644 --- a/qcsrc/server/w_hagar.qc +++ b/qcsrc/server/w_hagar.qc @@ -101,6 +101,7 @@ void W_Hagar_Attack (void) missile.angles = vectoangles (missile.velocity); missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE); @@ -144,6 +145,7 @@ void W_Hagar_Attack2 (void) missile.angles = vectoangles (missile.velocity); missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR_BOUNCING, TRUE); @@ -174,7 +176,7 @@ void W_Hagar_Attack2_Load_Release (void) shots = self.hagar_load; missile = world; - while (counter < shots) + for(counter = 0; counter < shots; ++counter) { missile = spawn (); missile.owner = missile.realowner = self; @@ -197,6 +199,7 @@ void W_Hagar_Attack2_Load_Release (void) setorigin (missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); missile.movetype = MOVETYPE_FLY; + missile.missile_flags = MIF_SPLASH; // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar) spread_pershot = ((shots - 1) / (autocvar_g_balance_hagar_secondary_load_max - 1)); @@ -223,8 +226,6 @@ void W_Hagar_Attack2_Load_Release (void) CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE); other = missile; MUTATOR_CALLHOOK(EditProjectile); - - counter = counter + 1; } weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hagar_secondary_load_animtime, w_ready); diff --git a/qcsrc/server/w_hlac.qc b/qcsrc/server/w_hlac.qc index 2eb2918ed6..a2697b2f5c 100644 --- a/qcsrc/server/w_hlac.qc +++ b/qcsrc/server/w_hlac.qc @@ -1,5 +1,5 @@ #ifdef REGISTER_WEAPON -REGISTER_WEAPON(HLAC, w_hlac, IT_CELLS, 6, WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "hlac", "hlac", _("Heavy Laser Assault Cannon")) +REGISTER_WEAPON(HLAC, w_hlac, IT_CELLS, 6, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "hlac", "hlac", _("Heavy Laser Assault Cannon")) #else #ifdef SVQC @@ -102,6 +102,7 @@ void W_HLAC_Attack2f (void) missile.nextthink = time + autocvar_g_balance_hlac_secondary_lifetime; missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; missile.projectiledeathtype = WEP_HLAC | HITTYPE_SECONDARY; CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE); diff --git a/qcsrc/server/w_hook.qc b/qcsrc/server/w_hook.qc index 695ac381fe..63d02604c4 100644 --- a/qcsrc/server/w_hook.qc +++ b/qcsrc/server/w_hook.qc @@ -100,6 +100,7 @@ void W_Hook_Attack2() gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale; gren.event_damage = W_Hook_Damage; gren.damagedbycontents = TRUE; + gren.missile_flags = MIF_SPLASH | MIF_ARC; gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed; if(autocvar_g_projectiles_newton_style) diff --git a/qcsrc/server/w_laser.qc b/qcsrc/server/w_laser.qc index 3da9e422b5..5e2bb075aa 100644 --- a/qcsrc/server/w_laser.qc +++ b/qcsrc/server/w_laser.qc @@ -75,6 +75,7 @@ void W_Laser_Attack (float issecondary) missile.touch = W_Laser_Touch; missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; missile.think = W_Laser_Think; missile.nextthink = time + autocvar_g_balance_laser_primary_delay; diff --git a/qcsrc/server/w_minelayer.qc b/qcsrc/server/w_minelayer.qc index 3ec50da50d..4d98f0c91c 100644 --- a/qcsrc/server/w_minelayer.qc +++ b/qcsrc/server/w_minelayer.qc @@ -1,5 +1,5 @@ #ifdef REGISTER_WEAPON -REGISTER_WEAPON(MINE_LAYER, w_minelayer, IT_ROCKETS, 4, WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_HIGH, "minelayer", "minelayer", _("Mine Layer")) +REGISTER_WEAPON(MINE_LAYER, w_minelayer, IT_ROCKETS, 4, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_HIGH, "minelayer", "minelayer", _("Mine Layer")) #else #ifdef SVQC void W_Mine_Think (void); @@ -149,7 +149,7 @@ void W_Mine_ProximityExplode () float W_Mine_Count(entity e) { - float minecount; + float minecount = 0; entity mine; for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e) minecount += 1; @@ -308,6 +308,7 @@ void W_Mine_Attack (void) mine.nextthink = time; mine.cnt = time + (autocvar_g_balance_minelayer_lifetime - autocvar_g_balance_minelayer_lifetime_countdown); mine.flags = FL_PROJECTILE; + mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY; CSQCProjectile(mine, TRUE, PROJECTILE_MINE, TRUE); @@ -325,12 +326,10 @@ void W_Mine_Attack (void) self.minelayer_mines = W_Mine_Count(self); } -void spawnfunc_weapon_minelayer (void); // defined in t_items.qc - float W_PlacedMines(float detonate) { entity mine; - float minfound; + float minfound = 0; for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == self) { diff --git a/qcsrc/server/w_minstanex.qc b/qcsrc/server/w_minstanex.qc index d8dc33ec66..1f1137e4b8 100644 --- a/qcsrc/server/w_minstanex.qc +++ b/qcsrc/server/w_minstanex.qc @@ -1,5 +1,5 @@ #ifdef REGISTER_WEAPON -REGISTER_WEAPON(MINSTANEX, w_minstanex, IT_CELLS, 7, WEP_FLAG_HIDDEN | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_HIGH, "minstanex", "minstanex", _("MinstaNex")) +REGISTER_WEAPON(MINSTANEX, w_minstanex, IT_CELLS, 7, WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_HIGH, "minstanex", "minstanex", _("MinstaNex")) #else #ifdef SVQC .float minstanex_lasthit; @@ -15,7 +15,9 @@ void W_MinstaNex_Attack (void) yoda = 0; damage_goodhits = 0; headshot = 0; + damage_headshotbonus = -1; // no extra damage, just count FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 10000, 800, 0, 0, 0, 0, WEP_MINSTANEX); + damage_headshotbonus = 0; if(g_minstagib) { diff --git a/qcsrc/server/w_porto.qc b/qcsrc/server/w_porto.qc index c919c497e6..f95e554b0e 100644 --- a/qcsrc/server/w_porto.qc +++ b/qcsrc/server/w_porto.qc @@ -36,7 +36,7 @@ void W_Porto_Fail (float failhard) self.realowner.porto_current = world; - if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPBIT_PORTO)) + if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !WEPSET_CONTAINS_EW(self.realowner, WEP_PORTO)) { setsize (self, '-16 -16 0', '16 16 32'); setorigin(self, self.origin + trace_plane_normal); @@ -202,12 +202,6 @@ void W_Porto_Attack (float type) { entity gren; - if(type == -1) - { - if not(self.items & IT_UNLIMITED_SUPERWEAPONS) - self.weapons = self.weapons - (self.weapons & WEPBIT_PORTO); - } - W_SetupShot (self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0); // always shoot from the eye w_shotdir = v_forward; @@ -278,7 +272,7 @@ void spawnfunc_weapon_porto (void) float w_nexball_weapon(float req); float w_porto(float req) { - vector v_angle_save; + //vector v_angle_save; if (g_nexball) { return w_nexball_weapon(req); } if (req == WR_AIM) @@ -332,7 +326,6 @@ float w_porto(float req) ClientData_Touch(self); } } - v_angle_save = self.v_angle; if(self.porto_v_angle_held) makevectors(self.porto_v_angle); // override the previously set angles diff --git a/qcsrc/server/w_rifle.qc b/qcsrc/server/w_rifle.qc index 89cb37257d..1c5f766d7d 100644 --- a/qcsrc/server/w_rifle.qc +++ b/qcsrc/server/w_rifle.qc @@ -1,5 +1,5 @@ #ifdef REGISTER_WEAPON -REGISTER_WEAPON(RIFLE, w_rifle, IT_NAILS, 7, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "rifle", _("Rifle")) +REGISTER_WEAPON(RIFLE, w_rifle, IT_NAILS, 7, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, BOT_PICKUP_RATING_MID, "campingrifle", "rifle", _("Rifle")) #else #ifdef SVQC diff --git a/qcsrc/server/w_rocketlauncher.qc b/qcsrc/server/w_rocketlauncher.qc index 42ae90d746..643bf4bd5b 100644 --- a/qcsrc/server/w_rocketlauncher.qc +++ b/qcsrc/server/w_rocketlauncher.qc @@ -250,6 +250,7 @@ void W_Rocket_Attack (void) missile.nextthink = time; missile.cnt = time + autocvar_g_balance_rocketlauncher_lifetime; missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; CSQCProjectile(missile, autocvar_g_balance_rocketlauncher_guiderate == 0 && autocvar_g_balance_rocketlauncher_speedaccel == 0, PROJECTILE_ROCKET, FALSE); // because of fly sound diff --git a/qcsrc/server/w_seeker.qc b/qcsrc/server/w_seeker.qc index 7cf9f58d8c..7cf6cfb736 100644 --- a/qcsrc/server/w_seeker.qc +++ b/qcsrc/server/w_seeker.qc @@ -1,5 +1,5 @@ #ifdef REGISTER_WEAPON -REGISTER_WEAPON(SEEKER, w_seeker, IT_ROCKETS, 8, WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "seeker", "seeker", _("T.A.G. Seeker")) +REGISTER_WEAPON(SEEKER, w_seeker, IT_ROCKETS, 8, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, BOT_PICKUP_RATING_MID, "seeker", "seeker", _("T.A.G. Seeker")) #else #ifdef SVQC //.float proxytime; = autoswitch @@ -78,6 +78,8 @@ void Seeker_Missile_Think() newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy self.velocity = newdir * spd; // make me fly in the new direction at my flight speed } + else + dist = 0; // Proxy if (autocvar_g_balance_seeker_missile_proxy) @@ -205,6 +207,8 @@ void Seeker_Fire_Missile(vector f_diff, entity m_target) setsize (missile, '-4 -4 -4', '4 4 4'); missile.movetype = MOVETYPE_FLYMISSILE; missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG; + W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_missile); missile.angles = vectoangles (missile.velocity); @@ -277,6 +281,7 @@ void Seeker_Fire_Flac() missile.projectiledeathtype = WEP_SEEKER; missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY; missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; // csqc projectiles //missile.angles = vectoangles (missile.velocity); @@ -308,6 +313,7 @@ void Seeker_Attack() { entity tracker, closest_target; + closest_target = world; for(tracker = world; (tracker = find(tracker, classname, "tag_tracker")); ) if (tracker.realowner == self) { if (closest_target) @@ -497,6 +503,7 @@ void Seeker_Fire_Tag() setsize (missile, '-2 -2 -2', '2 2 2'); missile.flags = FL_PROJECTILE; + //missile.missile_flags = MIF_..?; missile.movetype = MOVETYPE_FLY; W_SETUPPROJECTILEVELOCITY(missile, g_balance_seeker_tag); diff --git a/qcsrc/server/w_tuba.qc b/qcsrc/server/w_tuba.qc index a02061e8b8..999749b3e0 100644 --- a/qcsrc/server/w_tuba.qc +++ b/qcsrc/server/w_tuba.qc @@ -130,6 +130,9 @@ void W_Tuba_NoteOff() case 1: bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Accordeon: ^7", s, "\n")); break; + case 2: + bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Klein Bottle: ^7", s, "\n")); + break; } } } @@ -160,6 +163,7 @@ float Tuba_GetNote(entity pl, float hittype) case 2: note = -5; break; // G case 3: note = -4; break; // G# case 4: note = +5; break; // e# + default: case 5: note = 0; break; // c case 6: note = +2; break; // d case 7: note = +3; break; // eb @@ -373,9 +377,10 @@ float w_tuba(float req) precache_model ("models/weapons/g_tuba.md3"); precache_model ("models/weapons/v_tuba.md3"); precache_model ("models/weapons/h_tuba.iqm"); - precache_model ("models/weapons/g_akordeon.md3"); precache_model ("models/weapons/v_akordeon.md3"); precache_model ("models/weapons/h_akordeon.iqm"); + precache_model ("models/weapons/v_kleinbottle.md3"); + precache_model ("models/weapons/h_kleinbottle.iqm"); //float i; //for(i = -18; i <= +27; ++i) @@ -399,6 +404,10 @@ float w_tuba(float req) self.weaponname = "akordeon"; break; case 1: + self.tuba_instrument = 2; + self.weaponname = "kleinbottle"; + break; + case 2: self.tuba_instrument = 0; self.weaponname = "tuba"; break; @@ -446,6 +455,9 @@ float w_tuba(float req) case 1: // Accordeon w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Accordeon"); break; + case 2: // Klein Bottle + w_deathtypestring = _("%s hurt his own ears with the @!#%%'n Klein Bottle"); + break; } } else if (req == WR_KILLMESSAGE) @@ -467,6 +479,9 @@ float w_tuba(float req) case 1: // Accordeon w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Accordeon"); break; + case 2: // Klein Bottle + w_deathtypestring = _("%s died of %s's great playing on the @!#%%'n Klein Bottle"); + break; } } return TRUE; diff --git a/qcsrc/warpzonelib/client.qc b/qcsrc/warpzonelib/client.qc index 84bfe7b96d..6f75612343 100644 --- a/qcsrc/warpzonelib/client.qc +++ b/qcsrc/warpzonelib/client.qc @@ -2,7 +2,12 @@ void WarpZone_Fade_PreDraw() { vector org; org = getpropertyvec(VF_ORIGIN); - if(!checkpvs(org, self)) // this makes sense as long as we don't support recursive warpzones + if( +#ifdef COMPAT_XON060_DONTCRASH_CHECKPVS + cvar_string("g_xonoticversion") != "0.5.0" && + cvar_string("g_xonoticversion") != "0.6.0" && +#endif + !checkpvs(org, self)) // this makes sense as long as we don't support recursive warpzones self.alpha = 0; else if(self.warpzone_fadestart) self.alpha = bound(0, (self.warpzone_fadeend - vlen(org - self.origin - 0.5 * (self.mins + self.maxs))) / (self.warpzone_fadeend - self.warpzone_fadestart), 1); diff --git a/qcsrc/warpzonelib/common.qc b/qcsrc/warpzonelib/common.qc index f09f25bdcd..9040c72407 100644 --- a/qcsrc/warpzonelib/common.qc +++ b/qcsrc/warpzonelib/common.qc @@ -16,11 +16,19 @@ void WarpZone_Accumulator_AddTransform(entity acc, vector t, vector s) } void WarpZone_Accumulator_Add(entity acc, entity wz) { - vector t, st; - t = AnglesTransform_Multiply(wz.warpzone_transform, acc.warpzone_transform); - st = AnglesTransform_Multiply_GetPostShift(wz.warpzone_transform, wz.warpzone_shift, acc.warpzone_transform, acc.warpzone_shift); - acc.warpzone_transform = t; - acc.warpzone_shift = st; + WarpZone_Accumulator_AddTransform(acc, wz.warpzone_transform, wz.warpzone_shift); +} +void WarpZone_Accumulator_AddInverseTransform(entity acc, vector t, vector s) +{ + vector tt, ss; + tt = AnglesTransform_Invert(t); + ss = AnglesTransform_PrePostShift_GetPostShift(s, tt, '0 0 0'); + WarpZone_Accumulator_AddTransform(acc, tt, ss); + // yes, this probably can be done simpler... but this way is "obvious" :) +} +void WarpZone_Accumulator_AddInverse(entity acc, entity wz) +{ + WarpZone_Accumulator_AddInverseTransform(acc, wz.warpzone_transform, wz.warpzone_shift); } .vector(vector, vector) camera_transform; @@ -334,6 +342,7 @@ void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZo o0 = e.origin; v0 = e.velocity; + g = cvar("sv_gravity") * e.gravity; WarpZone_trace_forent = forent; WarpZone_trace_firstzone = world; @@ -376,7 +385,6 @@ void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZo e.velocity = WarpZone_TransformVelocity(wz, e.velocity); } WarpZone_MakeAllSolid(); - g = cvar("sv_gravity") * e.gravity; i = 16; for(;;) { @@ -643,7 +651,7 @@ void WarpZone_RefSys_GC() if(self.owner.WarpZone_refsys != self) remove(self); } -void WarpZone_RefSys_Add(entity me, entity wz) +void WarpZone_RefSys_CheckCreate(entity me) { if(me.WarpZone_refsys.owner != me) { @@ -654,29 +662,48 @@ void WarpZone_RefSys_Add(entity me, entity wz) me.WarpZone_refsys.nextthink = time + 1; WarpZone_Accumulator_Clear(me.WarpZone_refsys); } - if(wz) - WarpZone_Accumulator_Add(me.WarpZone_refsys, wz); +} +void WarpZone_RefSys_Clear(entity me) +{ + if(me.WarpZone_refsys) + { + remove(me.WarpZone_refsys); + me.WarpZone_refsys = world; + } +} +void WarpZone_RefSys_AddTransform(entity me, vector t, vector s) +{ + if(t != '0 0 0' || s != '0 0 0') + { + WarpZone_RefSys_CheckCreate(me); + WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s); + } +} +void WarpZone_RefSys_Add(entity me, entity wz) +{ + WarpZone_RefSys_AddTransform(me, wz.warpzone_transform, wz.warpzone_shift); +} +void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s) +{ + if(t != '0 0 0' || s != '0 0 0') + { + WarpZone_RefSys_CheckCreate(me); + WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, t, s); + } +} +void WarpZone_RefSys_AddInverse(entity me, entity wz) +{ + WarpZone_RefSys_AddInverseTransform(me, wz.warpzone_transform, wz.warpzone_shift); } .vector WarpZone_refsys_incremental_shift; .vector WarpZone_refsys_incremental_transform; void WarpZone_RefSys_AddIncrementally(entity me, entity ref) { - vector t, s; + //vector t, s; if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform) if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift) return; - if(me.WarpZone_refsys.owner != me) - { - me.WarpZone_refsys = spawn(); - me.WarpZone_refsys.classname = "warpzone_refsys"; - me.WarpZone_refsys.owner = me; - me.WarpZone_refsys.think = WarpZone_RefSys_GC; - me.WarpZone_refsys.nextthink = time + 1; - WarpZone_Accumulator_Clear(me.WarpZone_refsys); - } - t = AnglesTransform_Invert(me.WarpZone_refsys_incremental_transform); - s = AnglesTransform_PrePostShift_GetPostShift(me.WarpZone_refsys_incremental_shift, t, '0 0 0'); - WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s); + WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, me.WarpZone_refsys_incremental_transform, me.WarpZone_refsys_incremental_shift); WarpZone_Accumulator_Add(me.WarpZone_refsys, ref.WarpZone_refsys); me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift; me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform; @@ -718,19 +745,21 @@ vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang) ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang); return ang; } +void WarpZone_RefSys_Copy(entity me, entity from) +{ + if(from.WarpZone_refsys) + { + WarpZone_RefSys_CheckCreate(me); + me.WarpZone_refsys.warpzone_shift = from.WarpZone_refsys.warpzone_shift; + me.WarpZone_refsys.warpzone_transform = from.WarpZone_refsys.warpzone_transform; + } + else + WarpZone_RefSys_Clear(me); +} entity WarpZone_RefSys_SpawnSameRefSys(entity me) { entity e; e = spawn(); - if(me.WarpZone_refsys) - { - e.WarpZone_refsys = spawn(); - e.WarpZone_refsys.classname = "warpzone_refsys"; - e.WarpZone_refsys.owner = e; - e.WarpZone_refsys.think = WarpZone_RefSys_GC; - e.WarpZone_refsys.nextthink = time + 1; - e.WarpZone_refsys.warpzone_shift = me.WarpZone_refsys.warpzone_shift; - e.WarpZone_refsys.warpzone_transform = me.WarpZone_refsys.warpzone_transform; - } + WarpZone_RefSys_Copy(e, me); return e; } diff --git a/qcsrc/warpzonelib/common.qh b/qcsrc/warpzonelib/common.qh index c2f36bea86..e7cf23908e 100644 --- a/qcsrc/warpzonelib/common.qh +++ b/qcsrc/warpzonelib/common.qh @@ -62,14 +62,25 @@ vector WarpZone_UnTransformAngles(entity wz, vector v); vector WarpZone_UnTransformVAngles(entity wz, vector v); // reference systems (chained warpzone transforms) -void WarpZone_RefSys_Add(entity me, entity wz); -void WarpZone_RefSys_AddIncrementally(entity me, entity ref); -void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref); -vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org); -vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel); -vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang); -vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang); -entity WarpZone_RefSys_SpawnSameRefSys(entity me); +void WarpZone_RefSys_Clear(entity me); // R := id +void WarpZone_RefSys_Add(entity me, entity wz); // me.R := wz me.R +void WarpZone_RefSys_AddInverse(entity me, entity wz); // me.R := wz^-1 me.R +void WarpZone_RefSys_AddTransform(entity me, vector t, vector s); // me.R := [t s] me.R +void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s); // me.R := [t s]^-1 me.R + +// makes this reference system track ref's changes +// NOTE: this is ONLY sensible if WarpZone_RefSys_Add is no longer called on "me" while doing this +// To achieve this, make sure no touch events on warpzone are raised by this entity +// or set a movetype that causes no warpzoning (e.g. MOVETYPE_NONE, MOVETYPE_FOLLOW) +void WarpZone_RefSys_AddIncrementally(entity me, entity ref); // me.R := ref.R me.Rref^-1 me.R; me.Rref := ref.R +void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref); // me.Rref := ref.R + +vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org); // return to.R from.R^-1 org +vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel); // return to.R from.R^-1 vel +vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang +vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang +void WarpZone_RefSys_Copy(entity me, entity from); // to.R := from.R +entity WarpZone_RefSys_SpawnSameRefSys(entity me); // spawn().R = me.R #ifndef BITCLR # define BITCLR(a,b) ((a) - ((a) & (b))) diff --git a/qcsrc/warpzonelib/server.qc b/qcsrc/warpzonelib/server.qc index 89c4b72b5f..9b94640b5a 100644 --- a/qcsrc/warpzonelib/server.qc +++ b/qcsrc/warpzonelib/server.qc @@ -259,7 +259,7 @@ float WarpZone_Send(entity to, float sendflags) float WarpZone_Camera_Send(entity to, float sendflags) { - float f; + float f = 0; WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA); if(self.warpzone_fadestart) diff --git a/scripts/simpleitems.shader b/scripts/simpleitems.shader new file mode 100644 index 0000000000..69376e8609 --- /dev/null +++ b/scripts/simpleitems.shader @@ -0,0 +1,478 @@ +////////// AMMO ////////// + + +a_cells_simple // cells +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/a_cells_simple + blendfunc blend + } +} + +a_rockets_simple // rockets +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/a_rockets_simple + blendfunc blend + } +} + +a_bullets_simple // bullets +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/a_bullets_simple + blendfunc blend + + } +} + +a_shells_simple // shells +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/a_shells_simple + blendfunc blend + + } +} + +////////// WEAPONS ////////// + +g_crylink_simple // crylink +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_crylink_simple + blendfunc blend + + } +} + +g_electro_simple // electro +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_electro_simple + blendfunc blend + + } +} + +g_nex_simple // nex +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_nex_simple + blendfunc blend + + } +} + +g_hagar_simple // hagar +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_hagar_simple + blendfunc blend + + } +} + +g_gl_simple // mortar +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_gl_simple + blendfunc blend + + } +} + +g_rl_simple // rocket launcher +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_rl_simple + blendfunc blend + + } +} + +g_shotgun_simple // shotgun +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_shotgun_simple + blendfunc blend + + } +} + +g_uzi_simple // machine gun +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_uzi_simple + blendfunc blend + + } +} + +g_uzi_simple // machine gun +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_uzi_simple + blendfunc blend + + } +} + +g_campingrifle_simple // sniper rifle +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_campingrifle_simple + blendfunc blend + + } +} + +g_fireball_simple // fireball +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_fireball_simple + blendfunc blend + + } +} + +g_hlac_simple // HLAC +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_hlac_simple + blendfunc blend + + } +} + +g_hookgun_simple // hook gun +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_hookgun_simple + blendfunc blend + + } +} + +g_laser_simple // laser +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_laser_simple + blendfunc blend + + } +} + +g_minelayer_simple // minelayer +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_minelayer_simple + blendfunc blend + + } +} + +g_minstanex_simple // minstanex +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_minstanex_simple + blendfunc blend + + } +} + +g_porto_simple // portolauncher +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_porto_simple + blendfunc blend + + } +} + +g_seeker_simple // tag seeker +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_seeker_simple + blendfunc blend + + } +} + +g_tuba_simple // tuba +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_tuba_simple + blendfunc blend + + } +} + +////////// ARMOR + HEALTH ITEMS ////////// + +///// ARMOR ///// + +item_armor_small_simple // 5a +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/item_armor_small_simple + blendfunc blend + + } +} + +item_armor_medium_simple // 25a +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/item_armor_medium_simple + blendfunc blend + + } +} + +item_armor_big_simple // 50a +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/item_armor_big_simple + blendfunc blend + + } +} + +item_armor_large_simple // 100a +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/item_armor_large_simple + blendfunc blend + + } +} + +///// HEALTH ///// + +g_h1_simple // 5hp +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_h1_simple + blendfunc blend + + } +} + +g_h25_simple // 25hp +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_h25_simple + blendfunc blend + + } +} + +g_h50_simple // 50hp +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_h50_simple + blendfunc blend + + } +} + +g_h100_simple // 100hp +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_h100_simple + blendfunc blend + + } +} + +////////// POWERUPS ////////// + +g_strength_simple // strength +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_strength_simple + blendfunc blend + + } +} + +g_invincible_simple // shield +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_invincible_simple + blendfunc blend + + } +} + +g_jetpack_simple // jetpack +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_jetpack_simple + blendfunc blend + + } +} + +g_fuel_simple // fuel for jetpack +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_fuel_simple + blendfunc blend + + } +} + +g_fuelregen_simple // fuel regen powerup +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/items/g_fuelregen_simple + blendfunc blend + + } +} \ No newline at end of file diff --git a/scripts/weapons.shader b/scripts/weapons.shader index 9749b51cc2..a6dcfaab86 100644 --- a/scripts/weapons.shader +++ b/scripts/weapons.shader @@ -99,7 +99,7 @@ minelayer { dpreflectcube cubemaps/default/sky { - map textures/minelayer.tga + map textures/minelayer.tga rgbgen lightingDiffuse } } diff --git a/sound/ctf/pass.wav b/sound/ctf/pass.wav new file mode 100644 index 0000000000..0be02d52b8 Binary files /dev/null and b/sound/ctf/pass.wav differ diff --git a/sound/ctf/touch.wav b/sound/ctf/touch.wav new file mode 100644 index 0000000000..2ab908be37 Binary files /dev/null and b/sound/ctf/touch.wav differ diff --git a/sound/vehicles/missile_alarm.wav b/sound/vehicles/missile_alarm.wav new file mode 100644 index 0000000000..bf3860aaf8 Binary files /dev/null and b/sound/vehicles/missile_alarm.wav differ diff --git a/sound/weapons/tuba2_loopnote-12.ogg b/sound/weapons/tuba2_loopnote-12.ogg new file mode 100644 index 0000000000..f01bd0874e Binary files /dev/null and b/sound/weapons/tuba2_loopnote-12.ogg differ diff --git a/sound/weapons/tuba2_loopnote-18.ogg b/sound/weapons/tuba2_loopnote-18.ogg new file mode 100644 index 0000000000..e1ca02b0f1 Binary files /dev/null and b/sound/weapons/tuba2_loopnote-18.ogg differ diff --git a/sound/weapons/tuba2_loopnote-6.ogg b/sound/weapons/tuba2_loopnote-6.ogg new file mode 100644 index 0000000000..411cec7b5d Binary files /dev/null and b/sound/weapons/tuba2_loopnote-6.ogg differ diff --git a/sound/weapons/tuba2_loopnote0.ogg b/sound/weapons/tuba2_loopnote0.ogg new file mode 100644 index 0000000000..4924d59154 Binary files /dev/null and b/sound/weapons/tuba2_loopnote0.ogg differ diff --git a/sound/weapons/tuba2_loopnote12.ogg b/sound/weapons/tuba2_loopnote12.ogg new file mode 100644 index 0000000000..13b7c7fe68 Binary files /dev/null and b/sound/weapons/tuba2_loopnote12.ogg differ diff --git a/sound/weapons/tuba2_loopnote18.ogg b/sound/weapons/tuba2_loopnote18.ogg new file mode 100644 index 0000000000..20a29c2657 Binary files /dev/null and b/sound/weapons/tuba2_loopnote18.ogg differ diff --git a/sound/weapons/tuba2_loopnote24.ogg b/sound/weapons/tuba2_loopnote24.ogg new file mode 100644 index 0000000000..0e497b5737 Binary files /dev/null and b/sound/weapons/tuba2_loopnote24.ogg differ diff --git a/sound/weapons/tuba2_loopnote6.ogg b/sound/weapons/tuba2_loopnote6.ogg new file mode 100644 index 0000000000..652d51229a Binary files /dev/null and b/sound/weapons/tuba2_loopnote6.ogg differ diff --git a/sound/weapons/tuba_loopnote-1.ogg b/sound/weapons/tuba_loopnote-1.ogg deleted file mode 100644 index e296fe42b2..0000000000 Binary files a/sound/weapons/tuba_loopnote-1.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-10.ogg b/sound/weapons/tuba_loopnote-10.ogg deleted file mode 100644 index e14fca603a..0000000000 Binary files a/sound/weapons/tuba_loopnote-10.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-11.ogg b/sound/weapons/tuba_loopnote-11.ogg deleted file mode 100644 index e220a8a01b..0000000000 Binary files a/sound/weapons/tuba_loopnote-11.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-13.ogg b/sound/weapons/tuba_loopnote-13.ogg deleted file mode 100644 index 4545f5071e..0000000000 Binary files a/sound/weapons/tuba_loopnote-13.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-14.ogg b/sound/weapons/tuba_loopnote-14.ogg deleted file mode 100644 index ec0e27a39d..0000000000 Binary files a/sound/weapons/tuba_loopnote-14.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-15.ogg b/sound/weapons/tuba_loopnote-15.ogg deleted file mode 100644 index 2a540b0bdb..0000000000 Binary files a/sound/weapons/tuba_loopnote-15.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-16.ogg b/sound/weapons/tuba_loopnote-16.ogg deleted file mode 100644 index 9f0b1c8769..0000000000 Binary files a/sound/weapons/tuba_loopnote-16.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-17.ogg b/sound/weapons/tuba_loopnote-17.ogg deleted file mode 100644 index 6fbb1fc36f..0000000000 Binary files a/sound/weapons/tuba_loopnote-17.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-2.ogg b/sound/weapons/tuba_loopnote-2.ogg deleted file mode 100644 index 6089e4e9a2..0000000000 Binary files a/sound/weapons/tuba_loopnote-2.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-3.ogg b/sound/weapons/tuba_loopnote-3.ogg deleted file mode 100644 index 8299e6a152..0000000000 Binary files a/sound/weapons/tuba_loopnote-3.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-4.ogg b/sound/weapons/tuba_loopnote-4.ogg deleted file mode 100644 index fe452d1f6c..0000000000 Binary files a/sound/weapons/tuba_loopnote-4.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-5.ogg b/sound/weapons/tuba_loopnote-5.ogg deleted file mode 100644 index 48f412bc20..0000000000 Binary files a/sound/weapons/tuba_loopnote-5.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-7.ogg b/sound/weapons/tuba_loopnote-7.ogg deleted file mode 100644 index a71c9d9ba1..0000000000 Binary files a/sound/weapons/tuba_loopnote-7.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-8.ogg b/sound/weapons/tuba_loopnote-8.ogg deleted file mode 100644 index 45c7aad612..0000000000 Binary files a/sound/weapons/tuba_loopnote-8.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote-9.ogg b/sound/weapons/tuba_loopnote-9.ogg deleted file mode 100644 index 6bad19965a..0000000000 Binary files a/sound/weapons/tuba_loopnote-9.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote1.ogg b/sound/weapons/tuba_loopnote1.ogg deleted file mode 100644 index 43c7f66f97..0000000000 Binary files a/sound/weapons/tuba_loopnote1.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote10.ogg b/sound/weapons/tuba_loopnote10.ogg deleted file mode 100644 index a2e99dfe48..0000000000 Binary files a/sound/weapons/tuba_loopnote10.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote11.ogg b/sound/weapons/tuba_loopnote11.ogg deleted file mode 100644 index f9af7b0f74..0000000000 Binary files a/sound/weapons/tuba_loopnote11.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote13.ogg b/sound/weapons/tuba_loopnote13.ogg deleted file mode 100644 index ab865ac7d0..0000000000 Binary files a/sound/weapons/tuba_loopnote13.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote14.ogg b/sound/weapons/tuba_loopnote14.ogg deleted file mode 100644 index 42b649a92c..0000000000 Binary files a/sound/weapons/tuba_loopnote14.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote15.ogg b/sound/weapons/tuba_loopnote15.ogg deleted file mode 100644 index 4d8ba65ff4..0000000000 Binary files a/sound/weapons/tuba_loopnote15.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote16.ogg b/sound/weapons/tuba_loopnote16.ogg deleted file mode 100644 index fe5ea3aa23..0000000000 Binary files a/sound/weapons/tuba_loopnote16.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote17.ogg b/sound/weapons/tuba_loopnote17.ogg deleted file mode 100644 index 6351e36377..0000000000 Binary files a/sound/weapons/tuba_loopnote17.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote19.ogg b/sound/weapons/tuba_loopnote19.ogg deleted file mode 100644 index 56052bb8d6..0000000000 Binary files a/sound/weapons/tuba_loopnote19.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote2.ogg b/sound/weapons/tuba_loopnote2.ogg deleted file mode 100644 index 3945ca1395..0000000000 Binary files a/sound/weapons/tuba_loopnote2.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote20.ogg b/sound/weapons/tuba_loopnote20.ogg deleted file mode 100644 index 0eabe02311..0000000000 Binary files a/sound/weapons/tuba_loopnote20.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote21.ogg b/sound/weapons/tuba_loopnote21.ogg deleted file mode 100644 index aa28b41c8e..0000000000 Binary files a/sound/weapons/tuba_loopnote21.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote22.ogg b/sound/weapons/tuba_loopnote22.ogg deleted file mode 100644 index 621e82fc5a..0000000000 Binary files a/sound/weapons/tuba_loopnote22.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote23.ogg b/sound/weapons/tuba_loopnote23.ogg deleted file mode 100644 index 6edc26226e..0000000000 Binary files a/sound/weapons/tuba_loopnote23.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote25.ogg b/sound/weapons/tuba_loopnote25.ogg deleted file mode 100644 index d6b30440fb..0000000000 Binary files a/sound/weapons/tuba_loopnote25.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote26.ogg b/sound/weapons/tuba_loopnote26.ogg deleted file mode 100644 index 648f21755d..0000000000 Binary files a/sound/weapons/tuba_loopnote26.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote27.ogg b/sound/weapons/tuba_loopnote27.ogg deleted file mode 100644 index c5ddb4f1c6..0000000000 Binary files a/sound/weapons/tuba_loopnote27.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote3.ogg b/sound/weapons/tuba_loopnote3.ogg deleted file mode 100644 index 5ef1ce8980..0000000000 Binary files a/sound/weapons/tuba_loopnote3.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote4.ogg b/sound/weapons/tuba_loopnote4.ogg deleted file mode 100644 index 90ca19cb0e..0000000000 Binary files a/sound/weapons/tuba_loopnote4.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote5.ogg b/sound/weapons/tuba_loopnote5.ogg deleted file mode 100644 index 11512d1c61..0000000000 Binary files a/sound/weapons/tuba_loopnote5.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote7.ogg b/sound/weapons/tuba_loopnote7.ogg deleted file mode 100644 index 3138f29ec0..0000000000 Binary files a/sound/weapons/tuba_loopnote7.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote8.ogg b/sound/weapons/tuba_loopnote8.ogg deleted file mode 100644 index d2a4e48bf8..0000000000 Binary files a/sound/weapons/tuba_loopnote8.ogg and /dev/null differ diff --git a/sound/weapons/tuba_loopnote9.ogg b/sound/weapons/tuba_loopnote9.ogg deleted file mode 100644 index 9dd8c8697f..0000000000 Binary files a/sound/weapons/tuba_loopnote9.ogg and /dev/null differ diff --git a/sound/weapons/weaponpickup_new_toys.ogg b/sound/weapons/weaponpickup_new_toys.ogg new file mode 100644 index 0000000000..34660eef0e Binary files /dev/null and b/sound/weapons/weaponpickup_new_toys.ogg differ diff --git a/textures/raptor.tga b/textures/raptor.tga index 2037f7b10b..929805a30f 100644 Binary files a/textures/raptor.tga and b/textures/raptor.tga differ diff --git a/textures/raptor_pants.jpg b/textures/raptor_pants.jpg deleted file mode 100644 index 5cfa32aa0f..0000000000 Binary files a/textures/raptor_pants.jpg and /dev/null differ diff --git a/textures/raptor_shirt.jpg b/textures/raptor_shirt.jpg deleted file mode 100644 index e5bae42a29..0000000000 Binary files a/textures/raptor_shirt.jpg and /dev/null differ diff --git a/textures/raptor_shirt.tga b/textures/raptor_shirt.tga new file mode 100644 index 0000000000..5aa36ec11d Binary files /dev/null and b/textures/raptor_shirt.tga differ diff --git a/tooltips.db.fr b/tooltips.db.fr index 4762f8f2a8..34f0678066 100644 --- a/tooltips.db.fr +++ b/tooltips.db.fr @@ -28,6 +28,7 @@ \sv_vote_simple_majority_factor\À partir de 51% de oui seulement, un vote est gagné \XonoticMultiplayerDialog/Advanced settings...\Paramètres du serveur avancés \XonoticMultiplayerDialog/Mutators...\Spéciales et arènes d'une seule arme +\g_dodging\Enable dodging \g_cloaked\Tous les joueurs sont presque invisibles \g_footsteps\Activer les bruitages de pas \g_midair\Il faut que votre adversaire soit en l'air pour lui faire mal @@ -62,12 +63,10 @@ \cl_gunalign\Position de l'arme à l'écran, reconnection au serveur nécessaire pour prendre effet \crosshair_per_weapon\Set a different crosshair for each weapon, good if you play without weapon models -\crosshair_color_override\Also set the color of the crosshair depending on the weapon you are currently holding +\crosshair_color_per_weapon\Set the color of the crosshair depending on the weapon you are currently holding \crosshair_size\Ajuster la taille du viseur \crosshair_color_alpha\Ajuster l'opacité du viseur -\crosshair_color_red\Couleur: intensité du rouge dans le viseur -\crosshair_color_green\Couleur: intensité du vert dans le viseur -\crosshair_color_blue\Couleur: intensité du bleu dans le viseur +\crosshair_color\Ajuster le couleur du viseur \sbar_hudselector\Utiliser l'ancienne interface HUD \XonoticMultiplayerDialog/Waypoints setup...\- \_cl_name\Pseudonyme utilisé pour vous reconnaître dans le jeu @@ -95,14 +94,13 @@ \m_pitch\Inverser la souris sur l'axe vertical (mode jeu d'avion) \vid_dgamouse\Utiliser une souris DGA \con_closeontoggleconsole\Autoriser la fermeture de console avec la touche d'ouverture de console (sinon, Shift+Échap) -\sbar_showbinds\Afficher les actions possibles avec des touches/commandes -\cl_showpressedkeys\Afficher les touches qu'un joueur est en train d'appuyer \XonoticSettingsDialog/Vidéo\Video settings \vid_width\Résolution de l'écran \vid_bitsperpixel\Profondeur des couleurs: 16 bits est plus rapide, mais 32 bits est de meilleure qualité (recommandé) \vid_fullscreen\Activer le mode plein écran (par défaut: activé) \vid_vsync\Activer la syncronisation verticale pour éviter des problèmes d'affichage, limite le nombre maximum d'images par seconde (par défaut: désactivé) +\gl_texture_anisotropy\Qualité du filtrage anistrope (par défaut: 1x) \r_glsl\Activer les Shaders OpenGL 2.0 pour des effets de lumière améliorés \gl_vbo\Utiliser les VBOs pour stocker les modèles 3D statiques dans la mémoire pour une meilleure performance (par défaut: Points et Triangles) \r_depthfirst\Éviter des problèmes de profondeur de rendu en faisant un rendu de profondeur de la carte/joueurs avant le rendu "standard" (par défaut: désactivé) @@ -132,7 +130,6 @@ \cl_gentle\Remplacer les effets gore par des effets moins violents (par défaut: désactivé) \cl_nogibs\Réduire le nombre de Gibs ou les désactiver totalement (par défaut: beaucoup) \v_kicktime\Faire trembler la vue en recevant des dégâts (par défaut: 0) -\gl_texture_anisotropy\Qualité du filtrage anistrope (par défaut: 1x) \r_glsl_deluxemapping\Utiliser les effets lumineux avancés (par défaut: activé) \r_shadow_gloss\Utiliser le reflet des textures (par défaut: activé) \gl_flashblend\Enable faster but uglier dynamic lights by rendering bright coronas instead of real dynamic lights (default: disabled) @@ -150,8 +147,7 @@ \r_coronas_occlusionquery\Prendre en compte la visibilité pour les effets "Brillance Lumière" (par défaut: activé) \r_bloom\Activer un effet d'éblouissement plus beau, mais gourmand (par défaut: désactivé) \r_hdr\Activer un effet d'ébloissement encore plus beau, mais encore plus gourmand (par défaut: désactivé) -\r_motionblur\Intensité du flou de mouvement - 0.5 est recommandé -\r_damageblur\Intensité du flou en recevant des dégâts - 0.4 est recommandé +\r_motionblur\Intensité du flou de mouvement - 0.4 est recommandé \XonoticSettingsDialog/Audio\Audio settings \mastervolume\- @@ -178,7 +174,7 @@ \XonoticSettingsDialog/Réseau\Paramètres du jeu en réseau \cl_movement\Activer la prédiction des mouvements du joueur pour éviter les saccades lors de parties en réseau \cl_nolerp\Algorithme pour éviter les saccades lors de parties en réseau -\shownetgraph\Show a +\shownetgraph\Show a graph of packet sizes and other information \_cl_rate\Specify your network speed with this slider \cl_netfps\Nombre maximum de paquets à envoyer au server chaque seconde \cl_curl_maxdownloads\Nombre maximum de téléchargements simultanés @@ -186,32 +182,17 @@ \cl_port\Forcer le client à passer par le port défini (UDP) s'il n'est pas 0 \XonoticSettingsDialog/Autres\Autres paramètres +\menu_tooltips\Menu tooltips: disabled, standard or advanced (also shows cvar or console command bound to the menu item) \showtime\Montrer l'heure, utile pour les captures d'écran \showdate\Montrer la date, utile pour les captures d'écran \showfps\Montrer le nombre d'Images Par Seconde rendues (Frames Per Second = FPS) -\cl_showspeed\Montrer la vitesse du joueur -\cl_showspeed_unit\Séléctionner l'unité de mesure de la vitesse (qu/s = in/s) -\cl_showacceleration\Montrer l'accélération du joueur -\cl_showacceleration_scale\Éxagérer l'accéléromètre avec ce facteur pour qu'il soit plus visible \XonoticSettingsDialog/Advanced settings...\Paramètres avancés pour configuer le jeu dans ses moindres détails \g_friendlyfire\Pourcentage de dégâts infligés aux équipiers si vous les touchez \g_mirrordamage\Pourcentage de dégâts infligés aux équipiers qui vous sera renvoyé \g_tdm_teams_override\Écraser le nombre d'équipes défini par la carte -\cl_teamradar_position\- -\cl_teamradar_size\- -\cl_teamradar_zoommode\- -\cl_teamradar_rotation\- -\cl_teamradar_scale\- -\cl_teamradar_foreground_alpha\- -\cl_teamradar_background_alpha\Opacité de l'arrière-plan du radar \viewsize\Enable/Désactiver l'arrière plan de l'interface -\sbar_alpha_bg\Opacité de l'arrière plan de l'interface -\sbar_color_bg_r\Intensité du rouge dans l'arrière-plan de l'interface -\sbar_color_bg_g\Intensité du vert dans l'arrière-plan de l'interface -\sbar_color_bg_b\Intensité du bleu dans l'arrière-plan de l'interface -\sbar_color_bg_team\Saturation de la couleur d'équipe avec l'arrière-plan de l'interface \cl_hidewaypoints\Montrer/Cacher les Waypoints (flèches 3D) \g_waypointsprite_scale\Ajuster la taille des Waypoints \g_waypointsprite_alpha\Ajuster l'opacité des Waypoints diff --git a/tooltips.db.hu b/tooltips.db.hu index d19d905f8a..81347c7577 100644 --- a/tooltips.db.hu +++ b/tooltips.db.hu @@ -29,6 +29,7 @@ \XonoticMultiplayerDialog/Módosítók...\Módosítók és fegyverarénák \g_dodging\El tudsz szökkenni jobbra-balra a lövések elől \g_cloaked\Minden játékos majdnem láthatatlan +\g_footsteps\Enable footstep sounds \g_midair\Csak addig tudod az ellenséged megsebezni, amíg az levegőben tartózkodik \g_vampire\Annyi pont adódik életerődhöz, amennyi sebzést a másiknak okozol \g_bloodloss\Életerőpont, ami alatt a játékos elkábul a vérveszteségtől @@ -204,7 +205,7 @@ \r_bloom\A fényes testek felületén végigfutó halvány, derengő ragyogás engedélyezése FIGYELEM! Az opció bekapcsolása jelentősen ronthatja a grafikai teljesítményt, az opció bekapcsolása csak nagyon erős számítógéppel rendelkezők számára ajánlott! (alapértelmezett: letiltva). \r_water\Valós idejű tükröződések, portálok és fénytörések engedélyzése. FIGYELEM! Az opció bekapcsolása jelentősen ronthatja a grafikai teljesítményt, az opció bekapcsolása csak nagyon erős számítógéppel rendelkezők számára ajánlott! (alapértelmezett: letiltva) \r_water_resolutionmultiplier\Tükröződések/A csúszkával a valós idejű tükröződési effektek minőségét tudod szabályozni. (alapértelmezett: jó) -\r_motionblur\A csúszkával a mozgási elmosódás intenzivitását tudod szabályozni. Az ajánlott érték 0.5 +\r_motionblur\A csúszkával a mozgási elmosódás intenzivitását tudod szabályozni. Az ajánlott érték 0.4 \hud_postprocessing_maxbluralpha\Extra képernyő effektek engedélyezése, mint pl. víz alatti hullámok, Sebzésnövelő használata során extra kontrasztos látómező stb. //BEÁLLÍTÁSOK ABLAK HANG FÜL diff --git a/tooltips.db.ru b/tooltips.db.ru index aebb0b35b1..9a6d72fbc5 100644 --- a/tooltips.db.ru +++ b/tooltips.db.ru @@ -28,6 +28,7 @@ \sv_vote_simple_majority_factor\Простое большинство выигрывает голосование \XonoticMultiplayerDialog/Advanced settings...\Дополнительные серверные настройки \XonoticMultiplayerDialog/Mutators...\Мутаторы и арены оружий +\g_dodging\Enable dodging \g_cloaked\Все игроки почти невидимы \g_footsteps\Включить звуки шагов \g_midair\Только находящиеся в воздухе получают повреждения @@ -62,12 +63,10 @@ \cl_gunalign\Position of the weapon model; requires reconnect \crosshair_per_weapon\Set a different crosshair for each weapon, good if you play without weapon models -\crosshair_color_override\Also set the color of the crosshair depending on the weapon you are currently holding +\crosshair_color_per_weapon\Set the color of the crosshair depending on the weapon you are currently holding \crosshair_size\Настроить размер перекрестья \crosshair_color_alpha\Настроить прозрачность перекрестья -\crosshair_color_red\Красная составляющая цвета перекрестья -\crosshair_color_green\Зелёная составляющая цвета перекрестья -\crosshair_color_blue\Синяя составляющая цвета перекрестья +\crosshair_color\Adjust the crosshair color \sbar_hudselector\Use the old HUD layout \XonoticMultiplayerDialog/Waypoints setup...\- \_cl_name\Имя, под которым вы появитесь в игре @@ -95,14 +94,13 @@ \m_pitch\Обратить движения мыши по вертикальной оси \vid_dgamouse\Использовать DGA ввод для мыши \con_closeontoggleconsole\Использовать привязку для открытия консоли также и для её сокрытия -\sbar_showbinds\Отображать действия / привязанные клавиши в строках, показываемых во время игры -\cl_showpressedkeys\Показывать, какие кнопки движений нажимает игрок \XonoticSettingsDialog/Изображение\Настройки изображения \vid_width\Разрешение экрана \vid_bitsperpixel\Сколько бит на точку использовать для вывода, советуется 32 \vid_fullscreen\Включить полноэкранный режим (по умолчанию: включено) \vid_vsync\Enable vertical synchronization to prevent tearing, will cap your fps to the screen refresh rate (default: disabled) +\gl_texture_anisotropy\Anisotropic filtering quality (default: 1x) \r_glsl\Enable OpenGL 2.0 pixel shaders for lightning (default: enabled) \gl_vbo\Make use of Vertex Buffer Objects to store static geometry in video memory for faster rendering (default: Vertex and Triangles) \r_depthfirst\Eliminate overdraw by rendering a depth-only version of the scene before the normal rendering starts (default: disabled) @@ -132,7 +130,6 @@ \cl_gentle\Replace blood and gibs with content that does not have any gore effects (default: disabled) \cl_nogibs\Reduce the amount of gibs or remove them completely (default: lots) \v_kicktime\How long a view kick from damage lasts (default: 0) -\gl_texture_anisotropy\Anisotropic filtering quality (default: 1x) \r_glsl_deluxemapping\Use per-pixel lighting effects (default: enabled) \r_shadow_gloss\Enable the use of glossmaps on textures supporting it (default: enabled) \gl_flashblend\Enable faster but uglier dynamic lights by rendering bright coronas instead of real dynamic lights (default: disabled) @@ -150,8 +147,7 @@ \r_coronas_occlusionquery\Fade coronas according to visibility (default: enabled) \r_bloom\Enable bloom effect, which brightens the neighboring pixels of very bright pixels. Has a big impact on performance. (default: disabled) \r_hdr\Higher quality version of bloom, which has a huge impact on performance. (default: disabled) -\r_motionblur\Motion blur strength - 0.5 recommended -\r_damageblur\Amount of motion blur when hurt - 0.4 recommended +\r_motionblur\Motion blur strength - 0.4 recommended \XonoticSettingsDialog/Звук\Настройки звука \mastervolume\- @@ -186,32 +182,17 @@ \cl_port\Force client to use chosen port unless it is set to 0 \XonoticSettingsDialog/Разное\Разные настройки +\menu_tooltips\Menu tooltips: disabled, standard or advanced (also shows cvar or console command bound to the menu item) \showtime\Показывать текущее время, полезно на снимках экранов \showdate\Показывать текущую дату, полезно на снимках экранов \showfps\Show your rendered frames per second -\cl_showspeed\Показывать скорость игрока -\cl_showspeed_unit\Выбор единиц измерения спидометра. qu/s = quake units/second = дюйм/секунду -\cl_showacceleration\Показывать ускорение игрока -\cl_showacceleration_scale\Excaggerate the accelerometer by this scale multiplier \XonoticSettingsDialog/Advanced settings...\Продвинутые настройки, в которых можно подстроить каждую переменную игры \g_friendlyfire\Доля урона, получаемого союзниками от союзников \g_mirrordamage\Доля урона, наносимого союзнику, которая будет отражена на себя \g_tdm_teams_override\Заменить значение по умолчанию количества команд в командных играх -\cl_teamradar_position\- -\cl_teamradar_size\- -\cl_teamradar_zoommode\- -\cl_teamradar_rotation\- -\cl_teamradar_scale\- -\cl_teamradar_foreground_alpha\- -\cl_teamradar_background_alpha\Значение прозрачности фона радара \viewsize\Включить/выключить фон HUD -\sbar_alpha_bg\Значение прозрачности фона HUD -\sbar_color_bg_r\Красная составляющая цвета фона HUD -\sbar_color_bg_g\Зелёная составляющая цвета фона HUD -\sbar_color_bg_b\Синяя составляющая цвета фона HUD -\sbar_color_bg_team\Насыщенность командного цвета фона HUD \cl_hidewaypoints\Показывать различные отметки, определяемые видом игры \g_waypointsprite_scale\Множитель размера отметок \g_waypointsprite_alpha\Управление прозрачностью отметок diff --git a/tooltips.db.uk b/tooltips.db.uk index 18df45040b..31695a6315 100644 --- a/tooltips.db.uk +++ b/tooltips.db.uk @@ -100,6 +100,7 @@ \vid_bitsperpixel\Скільки використовувати бітів на піксель (BPP), рекомендується 32 \vid_fullscreen\На повний екран (за замовчуванням: увімкнуто) \vid_vsync\Вмикає вертикальну синхронізацію щоб запобігти розривам зображення, ваші FPS не будуть підніматися вище швидкості оновлення монітору (за замовчуванням: увімкнуто) +\gl_texture_anisotropy\Якість анізотропної фільтрації (за замовчуванням: 1x) \r_glsl\Вмикає піксельні шейдери OpenGL 2.0 для освітлення (за замовчуванням: увімкнуто) \gl_vbo\Використовувати Vertex Buffer Objects щоб зберігати нерухому геометрію у відеопам'яті для прискорення рендеренгу (за замовчуванням: вершини та трикутники) \r_depthfirst\Прибирає перекриття одних пікселів іншими створюючи спочатку версію сцени з лише глибиною (за замовчуванням: вимкнуто) @@ -129,7 +130,6 @@ \cl_gentle\Замінити шматки тіла та кров не жорстокими елементами (за замовчуванням: вимкнуто) \cl_nogibs\Зменшити кількість шматків тіла, або відключити їх зовсім (за замовчуванням: багато) \v_kicktime\Як довго триватиме поштовх від ураження (за замовчуванням: 0) -\gl_texture_anisotropy\Якість анізотропної фільтрації (за замовчуванням: 1x) \r_glsl_deluxemapping\Використовувати попіксельні ефекти освітлення (за замовчуванням: увімкнуто) \r_shadow_gloss\Вмикає використання мап блиску на текстурах, які їх підтримують (за замовчуванням: увімкнуто) \gl_flashblend\Вмикає швидкіше, але не таке гарне динамічне освітлення за допомогою яскравих корон, а не справжніх динамічних вогнів (за замовчуванням: вимкнуто) @@ -147,8 +147,7 @@ \r_coronas_occlusionquery\Прибирати корони відповідно з видимістю (за замовчуванням: увімкнуто) \r_bloom\Увімкнути ефект bloom, який освітлює ближні пікселі дуже яскравих пікселів. Має сильний вплив на продуктивність (за замовчуванням: вимкнуто) \r_hdr\Якісніша версія ефекту bloom, сильно впливає на продуктивність (за замовчуванням: вимкнуто) -\r_motionblur\Сила ефекту motion blur - рекомендується 0.5 -\r_damageblur\Кількість motion blur під час поранення - рекомендується 0.4 +\r_motionblur\Сила ефекту motion blur - рекомендується 0.4 \XonoticSettingsDialog/Звук\Налаштування аудіо \mastervolume\- @@ -183,6 +182,7 @@ \cl_port\Змушувати клієнта використовувати обраний порт, але тільки якщо значення не 0 \XonoticSettingsDialog/Різне\Різні налаштування +\menu_tooltips\Menu tooltips: disabled, standard or advanced (also shows cvar or console command bound to the menu item) \showtime\Показувати поточний час, корисно для скриншотів \showdate\Показувати поточну дату, корисно для скриншотів \showfps\Показувати лічильник кадрів за секунду diff --git a/vehicle_bumblebee.cfg b/vehicle_bumblebee.cfg index 0d4feb3da0..705d2a9883 100644 --- a/vehicle_bumblebee.cfg +++ b/vehicle_bumblebee.cfg @@ -1,40 +1,64 @@ -set g_vehicle_bumblebee_speed_forward 400 -set g_vehicle_bumblebee_speed_strafe 200 -set g_vehicle_bumblebee_speed_up 200 -set g_vehicle_bumblebee_speed_down 200 -set g_vehicle_bumblebee_turnspeed 72 -set g_vehicle_bumblebee_pitchspeed 36 -set g_vehicle_bumblebee_pitchlimit 15 -set g_vehicle_bumblebee_friction 0.75 +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.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 75 -set g_vehicle_bumblebee_cannon_radius 150 -set g_vehicle_bumblebee_cannon_refire 1.5 -set g_vehicle_bumblebee_cannon_speed 5000 -set g_vehicle_bumblebee_cannon_spread 0.0125 -set g_vehicle_bumblebee_cannon_force 400 -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 5 -set g_vehicle_bumblebee_cannon_turnlimit_out 45 - -set g_vehicle_bumblebee_respawntime 10 +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 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_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 diff --git a/vehicle_racer.cfg b/vehicle_racer.cfg index 33fb4140ba..5c3e06aff1 100644 --- a/vehicle_racer.cfg +++ b/vehicle_racer.cfg @@ -1,60 +1,61 @@ -set g_vehicle_racer_respawntime 25 +set g_vehicle_racer_respawntime 35 -set g_vehicle_racer_health 190 +set g_vehicle_racer_health 200 set g_vehicle_racer_health_regen 0 set g_vehicle_racer_health_regen_pause 0 -set g_vehicle_racer_shield 75 -set g_vehicle_racer_shield_regen 25 +set g_vehicle_racer_shield 100 +set g_vehicle_racer_shield_regen 30 set g_vehicle_racer_shield_regen_pause 1 -set g_vehicle_racer_energy 125 -set g_vehicle_racer_energy_regen 40 +set g_vehicle_racer_energy 100 +set g_vehicle_racer_energy_regen 50 set g_vehicle_racer_energy_regen_pause 1 -set g_vehicle_racer_speed_stop 2000 -set g_vehicle_racer_speed_forward 800 -set g_vehicle_racer_speed_strafe 500 -set g_vehicle_racer_speed_afterburn 2000 -set g_vehicle_racer_friction 0.4 -set g_vehicle_racer_afterburn_cost 60 // energy consumed per second +set g_vehicle_racer_speed_stop 2500 +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 5000 // 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_springlength 65 +set g_vehicle_racer_springlength 70 set g_vehicle_racer_collision_multiplier 0.05 set g_vehicle_racer_anglestabilizer 1.75 -set g_vehicle_racer_turnspeed 200 -set g_vehicle_racer_pitchspeed 100 +set g_vehicle_racer_turnspeed 220 +set g_vehicle_racer_pitchspeed 125 set g_vehicle_racer_maxpitch 25 -set g_vehicle_racer_turnroll 32 +set g_vehicle_racer_turnroll 30 -set g_vehicle_racer_cannon_speed 9000 -set g_vehicle_racer_cannon_damage 20 +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.1 -set g_vehicle_racer_cannon_cost 4 +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 1400 -set g_vehicle_racer_rocket_turnrate 0.17 -set g_vehicle_racer_rocket_damage 160 +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 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 1600 set g_vehicle_racer_rocket_locktarget 1 -set g_vehicle_racer_rocket_locking_time 0.4 -set g_vehicle_racer_rocket_locking_releasetime 1.6 -set g_vehicle_racer_rocket_locked_time 5 -set g_vehicle_racer_rocket_locked_maxangle 1.46 +set g_vehicle_racer_rocket_locking_time 0.35 +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_racer_blowup_coredamage 250 @@ -63,6 +64,6 @@ set g_vehicle_racer_blowup_forceintensity 250 set g_vehicle_racer_bouncefactor 0.25 // Factor of old velocity to keep after colission set g_vehicle_racer_bouncestop 0 // if != 0, New veloctiy after bounce = 0 if new velocity < this -set g_vehicle_racer_bouncepain "35 2 250" // "minspeed_for_pain speedchange_to_pain_factor max_damage" +set g_vehicle_racer_bouncepain "60 0.75 300" // "minspeed_for_pain speedchange_to_pain_factor max_damage" set g_vehicle_racer_mass 900 diff --git a/vehicle_raptor.cfg b/vehicle_raptor.cfg index 2a46dd2825..1efc2f1753 100644 --- a/vehicle_raptor.cfg +++ b/vehicle_raptor.cfg @@ -1,65 +1,72 @@ -set g_vehicle_raptor_respawntime 35 +set g_vehicle_raptor_respawntime 40 // 0: go where player aims, +forward etc relative to aim angles // 1: ignore aim for up/down movement. +forward always moved forward, +jump always moves up set g_vehicle_raptor_movestyle 1 -set g_vehicle_raptor_turnroll 0.1 +set g_vehicle_raptor_turnroll 0.4 -set g_vehicle_raptor_turnspeed 80 -set g_vehicle_raptor_pitchspeed 40 -set g_vehicle_raptor_pitchlimit 35 +set g_vehicle_raptor_takeofftime 1.5 -set g_vehicle_raptor_speed_forward 900 -set g_vehicle_raptor_speed_strafe 700 -set g_vehicle_raptor_speed_up 500 -set g_vehicle_raptor_speed_down 800 -set g_vehicle_raptor_friction 0.7 +set g_vehicle_raptor_turnspeed 200 +set g_vehicle_raptor_pitchspeed 50 +set g_vehicle_raptor_pitchlimit 45 + +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_time 0.5 set g_vehicle_raptor_bomblet_spread 0.4 -set g_vehicle_raptor_bomblet_damage 50 -set g_vehicle_raptor_bomblet_edgedamage 20 -set g_vehicle_raptor_bomblet_radius 310 +set g_vehicle_raptor_bomblet_damage 55 +set g_vehicle_raptor_bomblet_edgedamage 25 +set g_vehicle_raptor_bomblet_radius 350 set g_vehicle_raptor_bomblet_force 150 set g_vehicle_raptor_bomblet_explode_delay 0.4 set g_vehicle_raptor_bombs_refire 5 -set g_vehicle_raptor_cannon_turnspeed 90 +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 25 +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 50 +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 2000 -set g_vehicle_raptor_energy 30 -set g_vehicle_raptor_energy_regen 15 -set g_vehicle_raptor_energy_regen_pause 0.8 +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 150 set g_vehicle_raptor_health_regen 0 set g_vehicle_raptor_health_regen_pause 0 -set g_vehicle_raptor_shield 120 +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.5 500" +set g_vehicle_raptor_bouncepain "1 4 1000" set g_vehicle_raptor_mass 2200 diff --git a/vehicle_spiderbot.cfg b/vehicle_spiderbot.cfg index bb866037ad..0223b09e89 100644 --- a/vehicle_spiderbot.cfg +++ b/vehicle_spiderbot.cfg @@ -1,59 +1,59 @@ set g_vehicle_spiderbot_respawntime 45 -set g_vehicle_spiderbot_health 850 -set g_vehicle_spiderbot_health_regen 15 -set g_vehicle_spiderbot_health_regen_pause 10 +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 150 +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.2 +set g_vehicle_spiderbot_shield_regen_pause 0.35 set g_vehicle_spiderbot_energy 0 set g_vehicle_spiderbot_energy_regen 0 set g_vehicle_spiderbot_energy_regen_pause 0 -set g_vehicle_spiderbot_turnspeed 180 -set g_vehicle_spiderbot_head_turnspeed 120 -set g_vehicle_spiderbot_head_turnlimit 120 -set g_vehicle_spiderbot_head_pitchspeed 70 -set g_vehicle_spiderbot_head_pitchlimit_up 24 -set g_vehicle_spiderbot_head_pitchlimit_down -16 +set g_vehicle_spiderbot_turnspeed 90 +set g_vehicle_spiderbot_turnspeed_strafe 300 +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_movement_inertia 0.25 - -set g_vehicle_spiderbot_minigun_damage 16 -set g_vehicle_spiderbot_minigun_spread 0.015 -set g_vehicle_spiderbot_minigun_speed 50000 -set g_vehicle_spiderbot_minigun_refire 0.05 -set g_vehicle_spiderbot_minigun_ammo_cost 1 -set g_vehicle_spiderbot_minigun_ammo_max 200 -set g_vehicle_spiderbot_minigun_ammo_regen 15 +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.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 200 +set g_vehicle_spiderbot_minigun_ammo_regen 40 set g_vehicle_spiderbot_minigun_ammo_regen_pause 1 -set g_vehicle_spiderbot_springlength 150 -set g_vehicle_spiderbot_springup 5 -set g_vehicle_spiderbot_springblend 0.15 +set g_vehicle_spiderbot_springlength 150 +set g_vehicle_spiderbot_springup 20 +set g_vehicle_spiderbot_springblend 0.1 set g_vehicle_spiderbot_rocket_health 100 -set g_vehicle_spiderbot_rocket_damage 75 -set g_vehicle_spiderbot_rocket_edgedamage 15 +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 150 +set g_vehicle_spiderbot_rocket_radius 250 set g_vehicle_spiderbot_rocket_reload 4 -set g_vehicle_spiderbot_rocket_refire 0.2 -set g_vehicle_spiderbot_rocket_speed 1750 -set g_vehicle_spiderbot_rocket_turnrate 0.2 -set g_vehicle_spiderbot_rocket_noise 0.3 -set g_vehicle_spiderbot_rocket_lifetime 30 - -set g_vehicle_spiderbot_crush_dmg 50 -set g_vehicle_spiderbot_crush_force 50 - -set g_vehicle_spiderbot_mass 5000 +set g_vehicle_spiderbot_rocket_refire 0.1 +set g_vehicle_spiderbot_rocket_refire2 0.025 // volly +set g_vehicle_spiderbot_rocket_speed 3500 +set g_vehicle_spiderbot_rocket_turnrate 0.25 +set g_vehicle_spiderbot_rocket_noise 0.2 +set g_vehicle_spiderbot_rocket_lifetime 20 +set g_vehicle_spiderbot_rocket_spread 0.05 set g_vehicle_spiderbot_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 diff --git a/vehicles.cfg b/vehicles.cfg index e002321222..4030c64885 100644 --- a/vehicles.cfg +++ b/vehicles.cfg @@ -5,9 +5,10 @@ exec vehicle_raptor.cfg exec vehicle_spiderbot.cfg exec vehicle_bumblebee.cfg -//set g_vehicle_racer_respawntime 10 -//set g_vehicle_spiderbot_respawntime 10 -//set g_vehicle_raptor_respawntime 10 +set g_vehicle_racer 1 +set g_vehicle_spiderbot 1 +set g_vehicle_raptor 1 +set g_vehicle_bumblebee 1 set g_vehicles_crush_dmg 70 set g_vehicles_crush_force 50 @@ -16,4 +17,10 @@ set cl_vehicles_hudscale 0.5way set g_vehicles_delayspawn 1 set g_vehicles_delayspawn_jitter 10 -set g_vehicles_allow_flagcarry 1 + +set g_vehicles_nex_damagerate 0.5 +set g_vehicles_uzi_damagerate 0.65 +set g_vehicles_rifle_damagerate 1 +set g_vehicles_minstanex_damagerate 0.001 +set g_vehicles_tag_damagerate 2 + diff --git a/xonotic-credits.txt b/xonotic-credits.txt index 9129bfc466..1b0318f21c 100644 --- a/xonotic-credits.txt +++ b/xonotic-credits.txt @@ -141,7 +141,8 @@ SoulKeeper_p Stephan "esteel" Stahl The player with the unnecessarily long name Wolfgang "Blub\0" Bumiller - +Erik "Ablu" Schilling +BlaXpirit **Past Contributors Alexander "motorsep" Zubov Amos "torus" Dudley