aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/weapons
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
commit9a96d0bff56f1969c68bb52a2f33296095bdc67d (patch)
tree4175928e488632705692e3cccafa1a38dd854615 /Northstar.CustomServers/scripts/vscripts/weapons
parent27bd240871b7c0f2f49fef137718b2e3c208e3b4 (diff)
downloadNorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.tar.gz
NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.zip
move to new mod format
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/weapons')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_arc_cannon.nut1032
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_at_turrets.gnut284
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_ball_lightning.gnut363
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_cloaker.gnut121
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_grenade.nut604
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_particle_wall.gnut460
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_team_emp.gnut38
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_vortex.nut1983
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_weapon_dialogue.nut44
-rw-r--r--Northstar.CustomServers/scripts/vscripts/weapons/_weapon_utility.nut3966
10 files changed, 0 insertions, 8895 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_arc_cannon.nut b/Northstar.CustomServers/scripts/vscripts/weapons/_arc_cannon.nut
deleted file mode 100644
index 1601330c..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_arc_cannon.nut
+++ /dev/null
@@ -1,1032 +0,0 @@
-untyped
-
-global function ArcCannon_Init
-
-global function ArcCannon_PrecacheFX
-global function ArcCannon_Start
-global function ArcCannon_Stop
-global function ArcCannon_ChargeBegin
-global function ArcCannon_ChargeEnd
-global function FireArcCannon
-global function ArcCannon_HideIdleEffect
-#if SERVER
- global function AddToArcCannonTargets
- global function RemoveArcCannonTarget
- global function ConvertTitanShieldIntoBonusCharge
-#endif
-global function GetArcCannonChargeFraction
-
-global function IsEntANeutralMegaTurret
-global function CreateArcCannonBeam
-
-
-// Aiming & Range
-global const DEFAULT_ARC_CANNON_FOVDOT = 0.98 // First target must be within this dot to be zapped and start a chain
-global const DEFAULT_ARC_CANNON_FOVDOT_MISSILE = 0.95 // First target must be within this dot to be zapped and start a chain ( if it's a missile, we allow more leaniency )
-global const ARC_CANNON_RANGE_CHAIN = 400 // Max distance we can arc from one target to another
-global const ARC_CANNON_TITAN_RANGE_CHAIN = 900 // Max distance we can arc from one target to another
-global const ARC_CANNON_CHAIN_COUNT_MIN = 5 // Max number of chains at no charge
-global const ARC_CANNON_CHAIN_COUNT_MAX = 5 // Max number of chains at full charge
-global const ARC_CANNON_CHAIN_COUNT_NPC = 2 // Number of chains when an NPC fires the weapon
-global const ARC_CANNON_FORK_COUNT_MAX = 1 // Number of forks that can come out of one target to other targets
-global const ARC_CANNON_FORK_DELAY = 0.1
-
-global const ARC_CANNON_RANGE_CHAIN_BURN = 400
-global const ARC_CANNON_TITAN_RANGE_CHAIN_BURN = 900
-global const ARC_CANNON_CHAIN_COUNT_MIN_BURN = 100 // Max number of chains at no charge
-global const ARC_CANNON_CHAIN_COUNT_MAX_BURN = 100 // Max number of chains at full charge
-global const ARC_CANNON_CHAIN_COUNT_NPC_BURN = 10 // Number of chains when an NPC fires the weapon
-global const ARC_CANNON_FORK_COUNT_MAX_BURN = 10 // Number of forks that can come out of one target to other targets
-global const ARC_CANNON_BEAM_LIFETIME_BURN = 1
-
-// Visual settings
-global const ARC_CANNON_BOLT_RADIUS_MIN = 32 // Bolt radius at no charge ( not actually sure what this does to the beam lol )
-global const ARC_CANNON_BOLT_RADIUS_MAX = 640 // Bold radius at full charge ( not actually sure what this does to the beam lol )
-global const ARC_CANNON_BOLT_WIDTH_MIN = 1 // Bolt width at no charge
-global const ARC_CANNON_BOLT_WIDTH_MAX = 26 // Bolt width at full charge
-global const ARC_CANNON_BOLT_WIDTH_NPC = 8 // Bolt width when used by NPC
-global const ARC_CANNON_BEAM_COLOR = "150 190 255"
-global const ARC_CANNON_BEAM_LIFETIME = 0.75
-
-// Player Effects
-global const ARC_CANNON_TITAN_SCREEN_SFX = "Null_Remove_SoundHook"
-global const ARC_CANNON_PILOT_SCREEN_SFX = "Null_Remove_SoundHook"
-global const ARC_CANNON_EMP_DURATION_MIN = 0.1
-global const ARC_CANNON_EMP_DURATION_MAX = 1.8
-global const ARC_CANNON_EMP_FADEOUT_DURATION = 0.4
-global const ARC_CANNON_SCREEN_EFFECTS_MIN = 0.01
-global const ARC_CANNON_SCREEN_EFFECTS_MAX = 0.02
-global const ARC_CANNON_SCREEN_THRESHOLD = 0.3385
-global const ARC_CANNON_3RD_PERSON_EFFECT_MIN_DURATION = 0.2
-
-// Damage
-global const ARC_CANNON_DAMAGE_FALLOFF_SCALER = 0.75 // Amount of damage carried on to the next target in the chain lightning. If 0.75, then a target that would normally take 100 damage will take 75 damage if they are one chain deep, or 56 damage if 2 levels deep
-global const ARC_CANNON_DAMAGE_CHARGE_RATIO = 0.85 // What amount of charge is required for full damage.
-global const ARC_CANNON_DAMAGE_CHARGE_RATIO_BURN = 0.676 // What amount of charge is required for full damage.
-global const ARC_CANNON_CAPACITOR_CHARGE_RATIO = 1.0
-
-// Options
-global const ARC_CANNON_TARGETS_MISSILES = 1 // 1 = arc cannon zaps missiles that are active, 0 = missiles are ignored by arc cannon
-
-//Mods
-global const OVERCHARGE_MAX_SHIELD_DECAY = 0.2
-global const OVERCHARGE_SHIELD_DECAY_MULTIPLIER = 0.04
-global const OVERCHARGE_BONUS_CHARGE_FRACTION = 0.05
-
-global const SPLITTER_DAMAGE_FALLOFF_SCALER = 0.6
-global const SPLITTER_FORK_COUNT_MAX = 10
-
-global const ARC_CANNON_SIGNAL_DEACTIVATED = "ArcCannonDeactivated"
-global const ARC_CANNON_SIGNAL_CHARGEEND = "ArcCannonChargeEnd"
-
-global const ARC_CANNON_BEAM_EFFECT = $"wpn_arc_cannon_beam"
-global const ARC_CANNON_BEAM_EFFECT_MOD = $"wpn_arc_cannon_beam_mod"
-
-global const ARC_CANNON_FX_TABLE = "exp_arc_cannon"
-
-global const ArcCannonTargetClassnames = {
- [ "npc_drone" ] = true,
- [ "npc_dropship" ] = true,
- [ "npc_marvin" ] = true,
- [ "npc_prowler" ] = true,
- [ "npc_soldier" ] = true,
- [ "npc_soldier_heavy" ] = true,
- [ "npc_soldier_shield" ] = true,
- [ "npc_spectre" ] = true,
- [ "npc_stalker" ] = true,
- [ "npc_super_spectre" ] = true,
- [ "npc_titan" ] = true,
- [ "npc_turret_floor" ] = true,
- [ "npc_turret_mega" ] = true,
- [ "npc_turret_sentry" ] = true,
- [ "npc_frag_drone" ] = true,
- [ "player" ] = true,
- [ "prop_dynamic" ] = true,
- [ "prop_script" ] = true,
- [ "grenade_frag" ] = true,
- [ "rpg_missile" ] = true,
- [ "script_mover" ] = true,
- [ "turret" ] = true,
-}
-
-struct {
- array<string> missileCheckTargetnames = [
- // "Arc Pylon",
- "Arc Ball"
- ]
-} file;
-
-function ArcCannon_Init()
-{
- RegisterSignal( ARC_CANNON_SIGNAL_DEACTIVATED )
- RegisterSignal( ARC_CANNON_SIGNAL_CHARGEEND )
- PrecacheParticleSystem( ARC_CANNON_BEAM_EFFECT )
- PrecacheParticleSystem( ARC_CANNON_BEAM_EFFECT_MOD )
- PrecacheImpactEffectTable( ARC_CANNON_FX_TABLE )
-
- #if CLIENT
- AddDestroyCallback( "mp_titanweapon_arc_cannon", ClientDestroyCallback_ArcCannon_Stop )
- #else
- level._arcCannonTargetsArrayID <- CreateScriptManagedEntArray()
- #endif
-
- PrecacheParticleSystem( $"impact_arc_cannon_titan" )
-}
-
-function ArcCannon_PrecacheFX()
-{
- PrecacheParticleSystem( $"wpn_arc_cannon_electricity_fp" )
- PrecacheParticleSystem( $"wpn_arc_cannon_electricity" )
-
- PrecacheParticleSystem( $"wpn_muzzleflash_arc_cannon_fp" )
- PrecacheParticleSystem( $"wpn_muzzleflash_arc_cannon" )
-}
-
-function ArcCannon_Start( weapon )
-{
- expect entity( weapon )
- if ( !IsPilot( weapon.GetWeaponOwner() ) )
- {
- weapon.PlayWeaponEffectNoCull( $"wpn_arc_cannon_electricity_fp", $"wpn_arc_cannon_electricity", "muzzle_flash" )
- weapon.EmitWeaponSound( "arc_cannon_charged_loop" )
- }
- else
- {
- weapon.EmitWeaponSound_1p3p( "Arc_Rifle_charged_Loop_1P", "Arc_Rifle_charged_Loop_3P" )
- }
-}
-
-function ArcCannon_Stop( weapon, player = null )
-{
- expect entity( weapon )
- weapon.Signal( ARC_CANNON_SIGNAL_DEACTIVATED )
-
- weapon.StopWeaponEffect( $"wpn_arc_cannon_electricity_fp", $"wpn_arc_cannon_electricity" )
- weapon.StopWeaponSound( "arc_cannon_charged_loop" )
-}
-
-function ArcCannon_ChargeBegin( entity weapon )
-{
- #if SERVER
- if ( weapon.HasMod( "overcharge" ) )
- {
- entity weaponOwner = weapon.GetWeaponOwner()
- if ( weaponOwner.IsTitan() )
- {
- entity soul = weaponOwner.GetTitanSoul()
- thread ConvertTitanShieldIntoBonusCharge( soul, weapon )
- }
- }
- #endif
-
- #if CLIENT
- if ( !weapon.ShouldPredictProjectiles() )
- return
-
- entity weaponOwner = weapon.GetWeaponOwner()
- Assert( weaponOwner.IsPlayer() )
- weaponOwner.StartArcCannon();
- #endif
-}
-
-function ArcCannon_ChargeEnd( entity weapon, entity player = null )
-{
- #if SERVER
- if ( IsValid( weapon ) )
- weapon.Signal( ARC_CANNON_SIGNAL_CHARGEEND )
- #endif
-
- #if CLIENT
- if ( weapon.GetWeaponOwner() == GetLocalViewPlayer() )
- {
- entity weaponOwner
- if ( player != null )
- weaponOwner = player
- else
- weaponOwner = weapon.GetWeaponOwner()
-
- if ( IsValid( weaponOwner ) && weaponOwner.IsPlayer() )
- weaponOwner.StopArcCannon()
- }
- #endif
-}
-
-#if SERVER
-function ConvertTitanShieldIntoBonusCharge( entity soul, entity weapon )
-{
- weapon.EndSignal( ARC_CANNON_SIGNAL_CHARGEEND )
- weapon.EndSignal( "OnDestroy" )
-
- local maxShieldDecay = OVERCHARGE_MAX_SHIELD_DECAY
- local bonusChargeFraction = OVERCHARGE_BONUS_CHARGE_FRACTION
- local shieldDecayMultiplier = OVERCHARGE_SHIELD_DECAY_MULTIPLIER
- int shieldHealthMax = soul.GetShieldHealthMax()
- local chargeRatio = GetArcCannonChargeFraction( weapon )
-
- while( 1 )
- {
- if ( !IsValid( soul ) || !IsValid( weapon ) )
- break
-
- local baseCharge = GetWeaponChargeFrac( weapon ) // + GetOverchargeBonusChargeFraction()
- local charge = clamp ( baseCharge * ( 1 / chargeRatio ), 0.0, 1.0 )
- if ( charge < 1.0 || maxShieldDecay > 0)
- {
- int shieldHealth = soul.GetShieldHealth()
-
- //Slight inconsistency in server updates, this ensures it never takes too much.
- if ( shieldDecayMultiplier > maxShieldDecay )
- shieldDecayMultiplier = maxShieldDecay
- maxShieldDecay -= shieldDecayMultiplier
-
- local shieldDecayAmount = shieldHealthMax * shieldDecayMultiplier
- local newShieldAmount = shieldHealth - shieldDecayAmount
- soul.SetShieldHealth( max( newShieldAmount, 0 ) )
- soul.nextRegenTime = Time() + GetShieldRegenTime( soul )
-
- if ( shieldDecayAmount > shieldHealth )
- bonusChargeFraction = bonusChargeFraction * ( shieldHealth / shieldDecayAmount )
- weapon.SetWeaponChargeFraction( baseCharge + bonusChargeFraction )
- }
- wait 0.1
- }
-}
-#endif
-
-function FireArcCannon( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- local weaponScriptScope = weapon.GetScriptScope()
- local baseCharge = GetWeaponChargeFrac( weapon ) // + GetOverchargeBonusChargeFraction()
- local charge = clamp( baseCharge * ( 1 / GetArcCannonChargeFraction( weapon ) ), 0.0, 1.0 )
- float newVolume = GraphCapped( charge, 0.25, 1.0, 0.0, 1.0 )
-
- weapon.EmitWeaponNpcSound( LOUD_WEAPON_AI_SOUND_RADIUS_MP, 0.2 )
-
- weapon.PlayWeaponEffect( $"wpn_muzzleflash_arc_cannon_fp", $"wpn_muzzleflash_arc_cannon", "muzzle_flash" )
-
- local attachmentName = "muzzle_flash"
- local attachmentIndex = weapon.LookupAttachment( attachmentName )
- Assert( attachmentIndex >= 0 )
- local muzzleOrigin = weapon.GetAttachmentOrigin( attachmentIndex )
-
- //printt( "-------- FIRING ARC CANNON --------" )
-
- table firstTargetInfo = GetFirstArcCannonTarget( weapon, attackParams )
- if ( !IsValid( firstTargetInfo.target ) )
- FireArcNoTargets( weapon, attackParams, muzzleOrigin )
- else
- FireArcWithTargets( weapon, firstTargetInfo, attackParams, muzzleOrigin )
-
- return 1
-}
-
-table function GetFirstArcCannonTarget( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- entity owner = weapon.GetWeaponOwner()
- local coneHeight = weapon.GetMaxDamageFarDist()
-
- local angleToAxis = 2 // set this too high and auto-titans using it will error on FindVisibleEntitiesInCone
- array<entity> ignoredEntities = [ owner, weapon ]
- int traceMask = TRACE_MASK_SHOT
- int flags = VIS_CONE_ENTS_TEST_HITBOXES
- local antilagPlayer = null
- if ( owner.IsPlayer() )
- {
- angleToAxis = owner.GetAttackSpreadAngle() * 0.11
- antilagPlayer = owner
- }
-
- int ownerTeam = owner.GetTeam()
-
- // Get a missile target and a non-missile target in the cone that the player can zap
- // We do this in a separate check so we can use a wider cone to be more forgiving for targeting missiles
- table firstTargetInfo = {}
- firstTargetInfo.target <- null
- firstTargetInfo.hitLocation <- null
-
- for ( int i = 0; i < 2; i++ )
- {
- local missileCheck = i == 0
- local coneAngle = angleToAxis
- if ( missileCheck && owner.IsPlayer() ) // missile check only if owner is player
- coneAngle *= 8.0
-
- coneAngle = clamp( coneAngle, 0.1, 89.9 )
-
- array<VisibleEntityInCone> results = FindVisibleEntitiesInCone( attackParams.pos, attackParams.dir, coneHeight, coneAngle, ignoredEntities, traceMask, flags, antilagPlayer )
- foreach ( result in results )
- {
- entity visibleEnt = result.ent
-
- if ( !IsValid( visibleEnt ) )
- continue
-
- if ( visibleEnt.IsPhaseShifted() )
- continue
-
- local classname = IsServer() ? visibleEnt.GetClassName() : visibleEnt.GetSignifierName()
-
- if ( !( classname in ArcCannonTargetClassnames ) )
- continue
-
- if ( "GetTeam" in visibleEnt )
- {
- int visibleEntTeam = visibleEnt.GetTeam()
- if ( visibleEntTeam == ownerTeam )
- continue
- if ( IsEntANeutralMegaTurret( visibleEnt, ownerTeam ) )
- continue
- }
-
- expect string( classname )
- string targetname = visibleEnt.GetTargetName()
-
- if ( missileCheck && ( classname != "rpg_missile" && !file.missileCheckTargetnames.contains( targetname ) ) )
- continue
-
- if ( !missileCheck && ( classname == "rpg_missile" || file.missileCheckTargetnames.contains( targetname ) ) )
- continue
-
- firstTargetInfo.target = visibleEnt
- firstTargetInfo.hitLocation = result.visiblePosition
- break
- }
- }
- //Creating a whiz-by sound.
- weapon.FireWeaponBullet_Special( attackParams.pos, attackParams.dir, 1, 0, true, true, true, true, true, false, false )
-
- return firstTargetInfo
-}
-
-function FireArcNoTargets( entity weapon, WeaponPrimaryAttackParams attackParams, muzzleOrigin )
-{
- Assert( IsValid( weapon ) )
- entity player = weapon.GetWeaponOwner()
- local chargeFrac = GetWeaponChargeFrac( weapon )
- local beamVec = attackParams.dir * weapon.GetMaxDamageFarDist()
- local playerEyePos = player.EyePosition()
- TraceResults traceResults = TraceLineHighDetail( playerEyePos, (playerEyePos + beamVec), weapon, (TRACE_MASK_PLAYERSOLID_BRUSHONLY | TRACE_MASK_BLOCKLOS), TRACE_COLLISION_GROUP_NONE )
- local beamEnd = traceResults.endPos
-
- VortexBulletHit ornull vortexHit = VortexBulletHitCheck( player, playerEyePos, beamEnd )
- if ( vortexHit )
- {
- expect VortexBulletHit( vortexHit )
- #if SERVER
- entity vortexWeapon = vortexHit.vortex.GetOwnerWeapon()
- string className = vortexWeapon.GetWeaponClassName()
- if ( vortexWeapon && ( className == "mp_titanweapon_vortex_shield" || className == "mp_titanweapon_vortex_shield_ion" ) )
- {
- // drain the vortex shield
- VortexDrainedByImpact( vortexWeapon, weapon, null, null )
- }
- else if ( IsVortexSphere( vortexHit.vortex ) )
- {
- // do damage to vortex_sphere entities that isn't the titan "vortex shield"
- local damageNear = weapon.GetWeaponInfoFileKeyField( "damage_near_value" )
- local damage = damageNear * GraphCapped( chargeFrac, 0, 0.5, 0.0, 1.0 ) * 10 // do more damage the more charged the weapon is.
- VortexSphereDrainHealthForDamage( vortexHit.vortex, damage )
- }
- #endif
- beamEnd = vortexHit.hitPos
- }
-
- float radius = Graph( chargeFrac, 0, 1, ARC_CANNON_BOLT_RADIUS_MIN, ARC_CANNON_BOLT_RADIUS_MAX )
- local boltWidth = Graph( chargeFrac, 0, 1, ARC_CANNON_BOLT_WIDTH_MIN, ARC_CANNON_BOLT_WIDTH_MAX )
- if ( player.IsNPC() )
- boltWidth = ARC_CANNON_BOLT_WIDTH_NPC
- thread CreateArcCannonBeam( weapon, null, muzzleOrigin, beamEnd, player, ARC_CANNON_BEAM_LIFETIME, radius, boltWidth, 2, false, true )
-
- #if SERVER
- PlayImpactFXTable( expect vector( beamEnd ), player, ARC_CANNON_FX_TABLE, SF_ENVEXPLOSION_INCLUDE_ENTITIES )
- #endif
-}
-
-function FireArcWithTargets( entity weapon, table firstTargetInfo, WeaponPrimaryAttackParams attackParams, muzzleOrigin )
-{
- local beamStart = muzzleOrigin
- local beamEnd
- entity player = weapon.GetWeaponOwner()
- local chargeFrac = GetWeaponChargeFrac( weapon )
- float radius = Graph( chargeFrac, 0, 1, ARC_CANNON_BOLT_RADIUS_MIN, ARC_CANNON_BOLT_RADIUS_MAX )
- float boltWidth = Graph( chargeFrac, 0, 1, ARC_CANNON_BOLT_WIDTH_MIN, ARC_CANNON_BOLT_WIDTH_MAX )
- local maxChains
- local minChains
-
- if ( weapon.HasMod( "burn_mod_titan_arc_cannon" ) )
- {
- if ( player.IsNPC() )
- maxChains = ARC_CANNON_CHAIN_COUNT_NPC_BURN
- else
- maxChains = ARC_CANNON_CHAIN_COUNT_MAX_BURN
-
- minChains = ARC_CANNON_CHAIN_COUNT_MIN_BURN
- }
- else
- {
- if ( player.IsNPC() )
- maxChains = ARC_CANNON_CHAIN_COUNT_NPC
- else
- maxChains = ARC_CANNON_CHAIN_COUNT_MAX
-
- minChains = ARC_CANNON_CHAIN_COUNT_MIN
- }
-
- if ( !player.IsNPC() )
- maxChains = Graph( chargeFrac, 0, 1, minChains, maxChains )
-
- table zapInfo = {}
- zapInfo.weapon <- weapon
- zapInfo.player <- player
- zapInfo.muzzleOrigin <- muzzleOrigin
- zapInfo.radius <- radius
- zapInfo.boltWidth <- boltWidth
- zapInfo.maxChains <- maxChains
- zapInfo.chargeFrac <- chargeFrac
- zapInfo.zappedTargets <- {}
- zapInfo.zappedTargets[ firstTargetInfo.target ] <- true
- zapInfo.dmgSourceID <- weapon.GetDamageSourceID()
- local chainNum = 1
- thread ZapTargetRecursive( expect entity( firstTargetInfo.target), zapInfo, zapInfo.muzzleOrigin, expect vector( firstTargetInfo.hitLocation ), chainNum )
-}
-
-function ZapTargetRecursive( entity target, table zapInfo, beamStartPos, vector ornull firstTargetBeamEndPos = null, chainNum = 1 )
-{
- if ( !IsValid( target ) )
- return
-
- if ( !IsValid( zapInfo.weapon ) )
- return
-
- Assert( target in zapInfo.zappedTargets )
- if ( chainNum > zapInfo.maxChains )
- return
- vector beamEndPos
- if ( firstTargetBeamEndPos == null )
- beamEndPos = target.GetWorldSpaceCenter()
- else
- beamEndPos = expect vector( firstTargetBeamEndPos )
-
- waitthread ZapTarget( zapInfo, target, beamStartPos, beamEndPos, chainNum )
-
- // Get other nearby targets we can chain to
- #if SERVER
- if ( !IsValid( zapInfo.weapon ) )
- return
-
- var noArcing = expect entity( zapInfo.weapon ).GetWeaponInfoFileKeyField( "disable_arc" )
-
- if ( noArcing != null && noArcing == 1 )
- return // no chaining on new arc cannon
-
- // NOTE: 'target' could be invalid at this point (no corpse)
- array<entity> chainTargets = GetArcCannonChainTargets( beamEndPos, target, zapInfo )
- foreach( entity chainTarget in chainTargets )
- {
- local newChainNum = chainNum
- if ( chainTarget.GetClassName() != "rpg_missile" )
- newChainNum++
- zapInfo.zappedTargets[ chainTarget ] <- true
- thread ZapTargetRecursive( chainTarget, zapInfo, beamEndPos, null, newChainNum )
- }
-
- if ( IsValid( zapInfo.player ) && zapInfo.player.IsPlayer() && zapInfo.zappedTargets.len() >= 5 )
- {
- #if HAS_STATS
- if ( chainNum == 5 )
- UpdatePlayerStat( expect entity( zapInfo.player ), "misc_stats", "arcCannonMultiKills", 1 )
- #endif
- }
- #endif
-}
-
-function ZapTarget( zapInfo, target, beamStartPos, beamEndPos, chainNum = 1 )
-{
- expect entity( target )
- expect vector( beamStartPos )
- expect vector( beamEndPos )
-
- //DebugDrawLine( beamStartPos, beamEndPos, 255, 0, 0, true, 5.0 )
- local boltWidth = zapInfo.boltWidth
- if ( zapInfo.player.IsNPC() )
- boltWidth = ARC_CANNON_BOLT_WIDTH_NPC
- local firstBeam = ( chainNum == 1 )
- #if SERVER
- if ( firstBeam )
- {
- PlayImpactFXTable( beamEndPos, expect entity( zapInfo.player ), ARC_CANNON_FX_TABLE, SF_ENVEXPLOSION_INCLUDE_ENTITIES )
- }
- #endif
-
- thread CreateArcCannonBeam( zapInfo.weapon, target, beamStartPos, beamEndPos, zapInfo.player, ARC_CANNON_BEAM_LIFETIME, zapInfo.radius, boltWidth, 5, true, firstBeam )
-
- #if SERVER
- local isMissile = ( target.GetClassName() == "rpg_missile" )
- if ( !isMissile )
- wait ARC_CANNON_FORK_DELAY
- else
- wait 0.05
-
- local deathPackage = damageTypes.arcCannon
-
- float damageAmount
- int damageMin
- int damageMax
-
- int damageFarValue = eWeaponVar.damage_far_value
- int damageNearValue = eWeaponVar.damage_near_value
- int damageFarValueTitanArmor = eWeaponVar.damage_far_value_titanarmor
- int damageNearValueTitanArmor = eWeaponVar.damage_near_value_titanarmor
- if ( zapInfo.player.IsNPC() )
- {
- damageFarValue = eWeaponVar.npc_damage_far_value
- damageNearValue = eWeaponVar.npc_damage_near_value
- damageFarValueTitanArmor = eWeaponVar.npc_damage_far_value_titanarmor
- damageNearValueTitanArmor = eWeaponVar.npc_damage_near_value_titanarmor
- }
-
- if ( IsValid( target ) && IsValid( zapInfo.player ) )
- {
- bool hasFastPacitor = false
- bool noArcing = false
-
- if ( IsValid( zapInfo.weapon ) )
- {
- entity weap = expect entity( zapInfo.weapon )
- hasFastPacitor = weap.GetWeaponInfoFileKeyField( "push_apart" ) != null && weap.GetWeaponInfoFileKeyField( "push_apart" ) == 1
- noArcing = weap.GetWeaponInfoFileKeyField( "no_arcing" ) != null && weap.GetWeaponInfoFileKeyField( "no_arcing" ) == 1
- }
-
- if ( target.GetArmorType() == ARMOR_TYPE_HEAVY )
- {
- if ( IsValid( zapInfo.weapon ) )
- {
- entity weapon = expect entity( zapInfo.weapon )
- damageMin = weapon.GetWeaponSettingInt( damageFarValueTitanArmor )
- damageMax = weapon.GetWeaponSettingInt( damageNearValueTitanArmor )
- }
- else
- {
- damageMin = 100
- damageMax = zapInfo.player.IsNPC() ? 1200 : 800
- }
- }
- else
- {
- if ( IsValid( zapInfo.weapon ) )
- {
- entity weapon = expect entity( zapInfo.weapon )
- damageMin = weapon.GetWeaponSettingInt( damageFarValue )
- damageMax = weapon.GetWeaponSettingInt( damageNearValue )
- }
- else
- {
- damageMin = 120
- damageMax = zapInfo.player.IsNPC() ? 140 : 275
- }
-
- if ( target.IsNPC() )
- {
- damageMin *= 3 // more powerful against NPC humans so they die easy
- damageMax *= 3
- }
- }
-
-
- local chargeRatio = GetArcCannonChargeFraction( zapInfo.weapon )
- if ( IsValid( zapInfo.weapon ) && !zapInfo.weapon.GetWeaponSettingBool( eWeaponVar.charge_require_input ) )
- {
- // use distance for damage if the weapon auto-fires
- entity weapon = expect entity( zapInfo.weapon )
- float nearDist = weapon.GetWeaponSettingFloat( eWeaponVar.damage_near_distance )
- float farDist = weapon.GetWeaponSettingFloat( eWeaponVar.damage_far_distance )
-
- float dist = Distance( weapon.GetOrigin(), target.GetOrigin() )
- damageAmount = GraphCapped( dist, farDist, nearDist, damageMin, damageMax )
- }
- else
- {
- // Scale damage amount based on how many chains deep we are
- damageAmount = GraphCapped( zapInfo.chargeFrac, 0, chargeRatio, damageMin, damageMax )
- }
- local damageFalloff = ARC_CANNON_DAMAGE_FALLOFF_SCALER
- if ( IsValid( zapInfo.weapon ) && zapInfo.weapon.HasMod( "splitter" ) )
- damageFalloff = SPLITTER_DAMAGE_FALLOFF_SCALER
- damageAmount *= pow( damageFalloff, chainNum - 1 )
-
- local dmgSourceID = zapInfo.dmgSourceID
-
- // Update Later - This shouldn't be done here, this is not where we determine if damage actually happened to the target
- // move to Damaged callback instead
- if ( damageAmount > 0 )
- {
- float empDuration = GraphCapped( zapInfo.chargeFrac, 0, chargeRatio, ARC_CANNON_EMP_DURATION_MIN, ARC_CANNON_EMP_DURATION_MAX )
-
- if ( target.IsPlayer() && target.IsTitan() && !hasFastPacitor && !noArcing )
- {
- float empViewStrength = GraphCapped( zapInfo.chargeFrac, 0, chargeRatio, ARC_CANNON_SCREEN_EFFECTS_MIN, ARC_CANNON_SCREEN_EFFECTS_MAX )
-
- if ( target.IsTitan() && zapInfo.chargeFrac >= ARC_CANNON_SCREEN_THRESHOLD )
- {
- Remote_CallFunction_Replay( target, "ServerCallback_TitanEMP", empViewStrength, empDuration, ARC_CANNON_EMP_FADEOUT_DURATION )
- EmitSoundOnEntityOnlyToPlayer( target, target, ARC_CANNON_TITAN_SCREEN_SFX )
- }
- else if ( zapInfo.chargeFrac >= ARC_CANNON_SCREEN_THRESHOLD )
- {
- StatusEffect_AddTimed( target, eStatusEffect.emp, empViewStrength, empDuration, ARC_CANNON_EMP_FADEOUT_DURATION )
- EmitSoundOnEntityOnlyToPlayer( target, target, ARC_CANNON_PILOT_SCREEN_SFX )
- }
- }
-
- // Do 3rd person effect on the body
- asset effect
- string tag
- target.TakeDamage( damageAmount, zapInfo.player, zapInfo.player, { origin = beamEndPos, force = Vector(0,0,0), scriptType = deathPackage, weapon = zapInfo.weapon, damageSourceId = dmgSourceID } )
- //vector dir = Normalize( beamEndPos - beamStartPos )
- //vector velocity = dir * 600
- //PushPlayerAway( target, velocity )
- //PushPlayerAway( expect entity( zapInfo.player ), -velocity )
-
- if ( IsValid( zapInfo.weapon ) && hasFastPacitor )
- {
- if ( IsAlive( target ) && IsAlive( expect entity( zapInfo.player ) ) && target.IsTitan() )
- {
- float pushPercent = GraphCapped( damageAmount, damageMin, damageMax, 0.0, 1.0 )
-
- if ( pushPercent > 0.6 )
- PushPlayersApart( target, expect entity( zapInfo.player ), pushPercent * 400.0 )
- }
- }
-
- if ( zapInfo.chargeFrac < ARC_CANNON_SCREEN_THRESHOLD )
- empDuration = ARC_CANNON_3RD_PERSON_EFFECT_MIN_DURATION
- else
- empDuration += ARC_CANNON_EMP_FADEOUT_DURATION
-
- if ( target.GetArmorType() == ARMOR_TYPE_HEAVY )
- {
- effect = $"impact_arc_cannon_titan"
- tag = "exp_torso_front"
- }
- else
- {
- effect = $"P_emp_body_human"
- tag = "CHESTFOCUS"
- }
-
- if ( target.IsPlayer() )
- {
- if ( target.LookupAttachment( tag ) != 0 )
- ClientStylePlayFXOnEntity( effect, target, tag, empDuration )
- }
-
- if ( target.IsPlayer() )
- EmitSoundOnEntityExceptToPlayer( target, target, "Titan_Blue_Electricity_Cloud" )
- else
- EmitSoundOnEntity( target, "Titan_Blue_Electricity_Cloud" )
-
- thread FadeOutSoundOnEntityAfterDelay( target, "Titan_Blue_Electricity_Cloud", empDuration * 0.6666, empDuration * 0.3333 )
- }
- else
- {
- //Don't bounce if the beam is set to do 0 damage.
- chainNum = zapInfo.maxChains
- }
-
- if ( isMissile )
- {
- if ( IsValid( zapInfo.player ) )
- target.SetOwner( zapInfo.player )
- target.MissileExplode()
- }
- }
- #endif // SERVER
-}
-
-
-#if SERVER
-
-void function PushEntForTime( entity ent, vector velocity, float time )
-{
- ent.EndSignal( "OnDeath" )
- float endTime = Time() + time
- float startTime = Time()
- for ( ;; )
- {
- if ( Time() >= endTime )
- break
- float multiplier = Graph( Time(), startTime, endTime, 1.0, 0.0 )
- vector currentVel = ent.GetVelocity()
- currentVel += velocity * multiplier
- ent.SetVelocity( currentVel )
- WaitFrame()
- }
-}
-
-array<entity> function GetArcCannonChainTargets( vector fromOrigin, entity fromTarget, table zapInfo )
-{
- // NOTE: fromTarget could be null/invalid if it was a drone
- array<entity> results = []
- if ( !IsValid( zapInfo.player ) )
- return results
-
- int playerTeam = expect entity( zapInfo.player ).GetTeam()
- array<entity> allTargets = GetArcCannonTargetsInRange( fromOrigin, playerTeam, expect entity( zapInfo.weapon ) )
- allTargets = ArrayClosest( allTargets, fromOrigin )
-
- local viewVector
- if ( zapInfo.player.IsPlayer() )
- viewVector = zapInfo.player.GetViewVector()
- else
- viewVector = AnglesToForward( zapInfo.player.EyeAngles() )
-
- local eyePosition = zapInfo.player.EyePosition()
-
- foreach ( ent in allTargets )
- {
- local forkCount = ARC_CANNON_FORK_COUNT_MAX
- if ( zapInfo.weapon.HasMod( "splitter" ) )
- forkCount = SPLITTER_FORK_COUNT_MAX
- else if ( zapInfo.weapon.HasMod( "burn_mod_titan_arc_cannon" ) )
- forkCount = ARC_CANNON_FORK_COUNT_MAX_BURN
-
- if ( results.len() >= forkCount )
- break
-
- if ( ent.IsPhaseShifted() )
- continue
-
- if ( ent.IsPlayer() )
- {
- // Ignore players that are passing damage to their parent. This is to address zapping a friendly rodeo player
- local entParent = ent.GetParent()
- if ( IsValid( entParent ) && ent.kv.PassDamageToParent.tointeger() )
- continue
-
- // only chains to other titan players for now
- if ( !ent.IsTitan() )
- continue
- }
-
- if ( ent.GetClassName() == "script_mover" )
- continue
-
- if ( IsEntANeutralMegaTurret( ent, playerTeam ) )
- continue
-
- if ( !IsAlive( ent ) )
- continue
-
- // Don't consider targets that already got zapped
- if ( ent in zapInfo.zappedTargets )
- continue
-
- //Preventing the arc-cannon from firing behind.
- local vecToEnt = ( ent.GetWorldSpaceCenter() - eyePosition )
- vecToEnt.Norm()
- local dotVal = DotProduct( vecToEnt, viewVector )
- if ( dotVal < 0 )
- continue
-
- // Check if we can see them, they aren't behind a wall or something
- local ignoreEnts = []
- ignoreEnts.append( zapInfo.player )
- ignoreEnts.append( ent )
-
- foreach( zappedTarget, val in zapInfo.zappedTargets )
- {
- if ( IsValid( zappedTarget ) )
- ignoreEnts.append( zappedTarget )
- }
-
- TraceResults traceResult = TraceLineHighDetail( fromOrigin, ent.GetWorldSpaceCenter(), ignoreEnts, (TRACE_MASK_PLAYERSOLID_BRUSHONLY | TRACE_MASK_BLOCKLOS), TRACE_COLLISION_GROUP_NONE )
-
- // Trace failed, lets try an eye to eye trace
- if ( traceResult.fraction < 1 )
- {
- // 'fromTarget' may be invalid
- if ( IsValid( fromTarget ) )
- traceResult = TraceLineHighDetail( fromTarget.EyePosition(), ent.EyePosition(), ignoreEnts, (TRACE_MASK_PLAYERSOLID_BRUSHONLY | TRACE_MASK_BLOCKLOS), TRACE_COLLISION_GROUP_NONE )
- }
-
- if ( traceResult.fraction < 1 )
- continue
-
- // Enemy is in visible, and within range.
- if ( !results.contains( ent ) )
- results.append( ent )
- }
-
- //printt( "NEARBY TARGETS VALID AND VISIBLE:", results.len() )
-
- return results
-}
-#endif // SERVER
-
-bool function IsEntANeutralMegaTurret( ent, int playerTeam )
-{
- expect entity( ent )
-
- if ( ent.GetClassName() != "npc_turret_mega" )
- return false
- int entTeam = ent.GetTeam()
- if ( entTeam == playerTeam )
- return false
- if ( !IsEnemyTeam( playerTeam, entTeam ) )
- return true
-
- return false
-}
-
-function ArcCannon_HideIdleEffect( entity weapon, delay )
-{
- bool weaponOwnerIsPilot = IsPilot( weapon.GetWeaponOwner() )
- weapon.EndSignal( ARC_CANNON_SIGNAL_DEACTIVATED )
- if ( weaponOwnerIsPilot == false )
- {
- weapon.StopWeaponEffect( $"wpn_arc_cannon_electricity_fp", $"wpn_arc_cannon_electricity" )
- weapon.StopWeaponSound( "arc_cannon_charged_loop" )
- }
- wait delay
-
- if ( !IsValid( weapon ) )
- return
-
- entity weaponOwner = weapon.GetWeaponOwner()
- //The weapon can be valid, but the player isn't a Titan during melee execute.
- // JFS: threads with waits should just end on "OnDestroy"
- if ( !IsValid( weaponOwner ) )
- return
-
- if ( weapon != weaponOwner.GetActiveWeapon() )
- return
-
- if ( weaponOwnerIsPilot == false )
- {
- weapon.PlayWeaponEffectNoCull( $"wpn_arc_cannon_electricity_fp", $"wpn_arc_cannon_electricity", "muzzle_flash" )
- weapon.EmitWeaponSound( "arc_cannon_charged_loop" )
- }
- else
- {
- weapon.EmitWeaponSound_1p3p( "Arc_Rifle_charged_Loop_1P", "Arc_Rifle_charged_Loop_3P" )
- }
-}
-
-#if SERVER
-void function AddToArcCannonTargets( entity ent )
-{
- AddToScriptManagedEntArray( level._arcCannonTargetsArrayID, ent );
-}
-
-function RemoveArcCannonTarget( ent )
-{
- RemoveFromScriptManagedEntArray( level._arcCannonTargetsArrayID, ent )
-}
-
-array<entity> function GetArcCannonTargets( vector origin, int team )
-{
- array<entity> targets = GetScriptManagedEntArrayWithinCenter( level._arcCannonTargetsArrayID, team, origin, ARC_CANNON_TITAN_RANGE_CHAIN )
-
- if ( ARC_CANNON_TARGETS_MISSILES )
- targets.extend( GetProjectileArrayEx( "rpg_missile", TEAM_ANY, team, origin, ARC_CANNON_TITAN_RANGE_CHAIN ) )
-
- return targets
-}
-
-array<entity> function GetArcCannonTargetsInRange( vector origin, int team, entity weapon )
-{
- array<entity> allTargets = GetArcCannonTargets( origin, team )
- array<entity> targetsInRange
-
- float titanDistSq
- float distSq
- if ( weapon.HasMod( "burn_mod_titan_arc_cannon" ) )
- {
- titanDistSq = ARC_CANNON_TITAN_RANGE_CHAIN_BURN * ARC_CANNON_TITAN_RANGE_CHAIN_BURN
- distSq = ARC_CANNON_RANGE_CHAIN_BURN * ARC_CANNON_RANGE_CHAIN_BURN
- }
- else
- {
- titanDistSq = ARC_CANNON_TITAN_RANGE_CHAIN * ARC_CANNON_TITAN_RANGE_CHAIN
- distSq = ARC_CANNON_RANGE_CHAIN * ARC_CANNON_RANGE_CHAIN
- }
-
- foreach( target in allTargets )
- {
- float d = DistanceSqr( target.GetOrigin(), origin )
- float validDist = target.IsTitan() ? titanDistSq : distSq
- if ( d <= validDist )
- targetsInRange.append( target )
- }
-
- return targetsInRange
-}
-#endif // SERVER
-
-function CreateArcCannonBeam( weapon, target, startPos, endPos, player, lifeDuration = ARC_CANNON_BEAM_LIFETIME, radius = 256, boltWidth = 4, noiseAmplitude = 5, hasTarget = true, firstBeam = false )
-{
- Assert( startPos )
- Assert( endPos )
-
- //**************************
- // LIGHTNING BEAM EFFECT
- //**************************
- if ( weapon.HasMod( "burn_mod_titan_arc_cannon" ) )
- lifeDuration = ARC_CANNON_BEAM_LIFETIME_BURN
- // If it's the first beam and on client we do a special beam so it's lined up with the muzzle origin
- #if CLIENT
- if ( firstBeam )
- thread CreateClientArcBeam( weapon, endPos, lifeDuration, target )
- #endif
-
- #if SERVER
- // Control point sets the end position of the effect
- entity cpEnd = CreateEntity( "info_placement_helper" )
- SetTargetName( cpEnd, UniqueString( "arc_cannon_beam_cpEnd" ) )
- cpEnd.SetOrigin( endPos )
- DispatchSpawn( cpEnd )
-
- entity zapBeam = CreateEntity( "info_particle_system" )
- zapBeam.kv.cpoint1 = cpEnd.GetTargetName()
-
- zapBeam.SetValueForEffectNameKey( GetBeamEffect( weapon ) )
-
- zapBeam.kv.start_active = 0
- zapBeam.SetOwner( player )
- zapBeam.SetOrigin( startPos )
- if ( firstBeam )
- {
- zapBeam.kv.VisibilityFlags = (ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY) // everyone but owner
- zapBeam.SetParent( player.GetActiveWeapon(), "muzzle_flash", false, 0.0 )
- }
- DispatchSpawn( zapBeam )
-
- zapBeam.Fire( "Start" )
- zapBeam.Fire( "StopPlayEndCap", "", lifeDuration )
- zapBeam.Kill_Deprecated_UseDestroyInstead( lifeDuration )
- cpEnd.Kill_Deprecated_UseDestroyInstead( lifeDuration )
- #endif
-}
-
-function GetBeamEffect( weapon )
-{
- if ( weapon.HasMod( "burn_mod_titan_arc_cannon" ) )
- return ARC_CANNON_BEAM_EFFECT_MOD
-
- return ARC_CANNON_BEAM_EFFECT
-}
-
-#if CLIENT
-function CreateClientArcBeam( weapon, endPos, lifeDuration, target )
-{
- Assert( IsClient() )
-
- local beamEffect = GetBeamEffect( weapon )
-
- // HACK HACK HACK HACK
- string tag = "muzzle_flash"
- if ( weapon.GetWeaponInfoFileKeyField( "client_tag_override" ) != null )
- tag = expect string( weapon.GetWeaponInfoFileKeyField( "client_tag_override" ) )
-
- local handle = weapon.PlayWeaponEffectReturnViewEffectHandle( beamEffect, $"", tag )
- if ( !EffectDoesExist( handle ) )
- return
-
- EffectSetControlPointVector( handle, 1, endPos )
-
- if ( weapon.HasMod( "burn_mod_titan_arc_cannon" ) )
- lifeDuration = ARC_CANNON_BEAM_LIFETIME_BURN
-
- wait( lifeDuration )
-
- if ( IsValid( weapon ) )
- weapon.StopWeaponEffect( beamEffect, $"" )
-}
-
-void function ClientDestroyCallback_ArcCannon_Stop( entity ent )
-{
- ArcCannon_Stop( ent )
-}
-#endif // CLIENT
-
-function GetArcCannonChargeFraction( weapon )
-{
- if ( IsValid( weapon ) )
- {
- local chargeRatio = ARC_CANNON_DAMAGE_CHARGE_RATIO
- if ( weapon.HasMod( "capacitor" ) )
- chargeRatio = ARC_CANNON_CAPACITOR_CHARGE_RATIO
- if ( weapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- chargeRatio = ARC_CANNON_DAMAGE_CHARGE_RATIO_BURN
- return chargeRatio
- }
-
- return 0
-}
-
-function GetWeaponChargeFrac( weapon )
-{
- if ( weapon.IsChargeWeapon() )
- return weapon.GetWeaponChargeFraction()
- return 1.0
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_at_turrets.gnut b/Northstar.CustomServers/scripts/vscripts/weapons/_at_turrets.gnut
deleted file mode 100644
index b061c182..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_at_turrets.gnut
+++ /dev/null
@@ -1,284 +0,0 @@
-untyped
-
-global function ATTurrets_Init
-global function CreateATTurret
-global function ATTurretSettings
-//global function SetDriverOnTurret
-global function PROTO_ATTurretsEnabled
-global function PROTO_Simulate_Turret_Use
-
-const USE_DEBOUNCE_TIME = 0.3
-const FX_ANTI_TITAN_SHIELD_WALL = $"P_anti_titan_shield_3P"
-const vector AT_TURRET_SHIELD_COLOR = Vector( 115, 247, 255 )
-
-void function ATTurrets_Init()
-{
- AddSpawnCallbackEditorClass( "turret", "turret_pilot_at", ATTurretSettings )
- RegisterSignal( "ClearDriver" )
- RegisterSignal( "DismebarkATTurret" )
-}
-
-void function CreateATTurret( vector origin, vector angles )
-{
- entity turret = CreateEntity( "turret" )
- turret.kv.editorclass = "turret_pilot_at"
- turret.kv.settings = "PROTO_at_turret"
- turret.kv.teamnumber = 0
- turret.SetValueForModelKey( $"models/weapons/sentry_turret/sentry_turret.mdl" )
- turret.kv.origin = origin
- turret.kv.angles = angles
- DispatchSpawn( turret )
- ATTurretSettings( turret )
-}
-
-void function ATTurretSettings( entity turret )
-{
- if ( PROTO_ATTurretsEnabled() )
- {
- turret.SetUsable()
- turret.SetUsableByGroup( "pilot" )
- turret.SetUsePrompts( "Hold %use% to use AT-Turret", "Press %use% to use AT-Turret" )
- //AddCallback_OnUseEntity( turret, SetDriverOnTurret )
- AddCallback_OnUseEntity( turret, PROTO_Simulate_Turret_Use )
-
- local attachmentID = turret.LookupAttachment( "muzzle_flash" )
- local origin = turret.GetAttachmentOrigin( attachmentID )
- local angles = turret.GetAttachmentAngles( attachmentID )
-
- entity cpoint = CreateEntity( "info_placement_helper" )
- SetTargetName( cpoint, UniqueString( "shield_wall_controlpoint" ) )
- DispatchSpawn( cpoint )
-
- turret.e.shieldWallFX = CreateEntity( "info_particle_system" )
- entity shieldWallFX = turret.e.shieldWallFX
- shieldWallFX.SetValueForEffectNameKey( FX_ANTI_TITAN_SHIELD_WALL )
- shieldWallFX.kv.start_active = 1
- SetShieldWallCPoint( shieldWallFX, cpoint )
- shieldWallFX.SetOwner( turret )
- shieldWallFX.kv.VisibilityFlags = (ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY) // not owner only
- shieldWallFX.kv.cpoint1 = cpoint.GetTargetName()
- shieldWallFX.SetStopType( "destroyImmediately" )
- shieldWallFX.SetOrigin( origin )
- shieldWallFX.SetAngles( angles - Vector(0,0,90) )
- shieldWallFX.SetParent( turret, "muzzle_flash", true, 0.0 )
- DispatchSpawn( shieldWallFX )
- SetShieldWallCPointOrigin( shieldWallFX, AT_TURRET_SHIELD_COLOR )
- }
- else
- {
- turret.DisableDraw()
- turret.NotSolid()
- }
-}
-
-bool function PROTO_ATTurretsEnabled()
-{
- return ( GetCurrentPlaylistVarInt( "at_turrets_enabled", 0 ) == 1 )
-}
-
-/*/////////////////////////////////////////////////////////////////
- WEAPON PROTOTYPE
-///////////////////////////////////////////////////////////////////*/
-
-function PROTO_Simulate_Turret_Use( turret, player )
-{
- expect entity( turret )
- expect entity( player )
-
- if ( Time() < player.p.PROTO_UseDebounceEndTime )
- return
-
- PROTO_ActivateTurret( turret, player )
-}
-
-const array<int> TURRET_CANCEL_BUTTONS =
-[
- IN_USE,
- IN_DUCK,
- IN_DUCKTOGGLE,
- IN_WEAPON_CYCLE,
- IN_MELEE,
- IN_OFFHAND0,
- IN_OFFHAND1,
- IN_OFFHAND2,
- IN_OFFHAND3,
- IN_OFFHAND4,
- IN_JUMP
-]
-
-void function PROTO_ActivateTurret( entity turret, entity player )
-{
- if ( turret.GetOwner() == player )
- {
- player.Signal( "DismebarkATTurret" )
- }
- else
- {
- if ( turret.GetOwner() == null )
- {
- turret.DisableDraw()
- turret.NotSolid()
- SetShieldWallCPointOrigin( turret.e.shieldWallFX, < 0, 0, 0 > )
- turret.SetOwner( player )
- player.p.PROTO_UseDebounceEndTime = Time() + USE_DEBOUNCE_TIME
- foreach( int button in TURRET_CANCEL_BUTTONS )
- AddButtonPressedPlayerInputCallback( player, button, PROTO_DisembarkATTurret )
- AddEntityCallback_OnDamaged( player, PlayerDamagedWhileOnTurret )
- thread MonitorPilot( turret, player )
- }
- else
- {
- SendHudMessage( player, "Turret in use.", -1, 0.4, 255, 255, 0, 255, 0.0, 0.5, 0.0 )
- }
- }
-}
-
-void function PlayerDamagedWhileOnTurret( entity player, var damageInfo )
-{
- if ( Time() < player.p.PROTO_UseDebounceEndTime )
- return
-
- player.Signal( "DismebarkATTurret" )
-}
-
-function MonitorPilot( entity turret, entity player )
-{
- player.EndSignal( "OnDestroy" )
- player.EndSignal( "DismebarkATTurret")
- turret.EndSignal( "OnDestroy" )
-
- player.ForceStand()
- entity playerMover = CreateOwnedScriptMover( player )
- player.SetParent( playerMover, "ref", true )
- vector forward = turret.GetForwardVector()
- vector basePos = turret.GetOrigin() + forward * -25
- vector startOrigin = player.GetOrigin()
- float moveTime = 0.1
- playerMover.NonPhysicsMoveTo( basePos, moveTime, 0.0, 0.0 )
- playerMover.NonPhysicsRotateTo( turret.GetAngles(), moveTime, 0, 0 )
- player.FreezeControlsOnServer()
-
- StorePilotWeapons( player )
-
- OnThreadEnd(
- function() : ( turret, player, playerMover, startOrigin )
- {
- if ( IsValid( player ) )
- {
- player.ClearParent()
- player.UnforceStand()
- ClearPlayerAnimViewEntity( player )
- player.UnfreezeControlsOnServer()
- RetrievePilotWeapons( player )
- ViewConeZeroInstant( player )
- foreach( int button in TURRET_CANCEL_BUTTONS )
- RemoveButtonPressedPlayerInputCallback( player, button, PROTO_DisembarkATTurret )
- RemoveEntityCallback_OnDamaged( player, PlayerDamagedWhileOnTurret )
- player.p.PROTO_UseDebounceEndTime = Time() + USE_DEBOUNCE_TIME
- PutEntityInSafeSpot( player, turret, null, startOrigin, player.GetOrigin() )
- }
-
- if ( IsValid( turret ) )
- {
- turret.EnableDraw()
- turret.Solid()
- SetShieldWallCPointOrigin( turret.e.shieldWallFX, AT_TURRET_SHIELD_COLOR )
- turret.SetOwner( null )
- }
-
- playerMover.Destroy()
- }
- )
-
- wait moveTime
-
- player.PlayerCone_SetSpecific( forward )
- ViewConeZeroInstant( player )
-
- // PROTO: Supporting ability to pick different turret weapons for turrets in LevelEd and the legacy Defender prototype turret
- // We need a predator cannon style turret in SP.
- if ( IsMultiplayer() )
- {
- //player.GiveWeapon( "mp_weapon_smr", [ "PROTO_at_turret" ] )
- //player.SetActiveWeaponByName( "mp_weapon_smr" )
-
- // modded code: smr's at turret mod does exist in release tf2
- player.GiveWeapon( "mp_weapon_defender", [ "PROTO_at_turret" ] )
- player.SetActiveWeaponByName( "mp_weapon_defender" )
- }
- else if ( turret.HasKey( "weaponsettings" ) )
- {
- // See if we have any special turret mods on this weapon
- array<string> turretMods = []
- array<string> mods = GetWeaponMods_Global( turret.kv.weaponsettings )
- foreach ( mod in mods )
- {
- if ( mod.find( "PROTO_at_turret" ) == 0 )
- turretMods.append( "PROTO_at_turret" )
- }
-
- player.GiveWeapon( turret.kv.weaponsettings, turretMods )
- player.SetActiveWeaponByName( turret.kv.weaponsettings )
- }
-
- wait 0.1
-
- player.UnfreezeControlsOnServer()
-
- ViewConeLockedForward( player )
-
- player.WaitSignal( "OnDeath" )
-}
-
-void function PROTO_DisembarkATTurret( entity player )
-{
- if ( Time() < player.p.PROTO_UseDebounceEndTime )
- return
-
- player.Signal( "DismebarkATTurret" )
-}
-
-/*/////////////////////////////////////////////////////////////////
- TURRET ENTITY PROTOTYPE
-///////////////////////////////////////////////////////////////////*/
-//
-//function SetDriverOnTurret( turret, player )
-//{
-// if ( turret.GetOwner() == player )
-// {
-// turret.SetOwner( null )
-// turret.ClearDriver()
-// player.Signal( "ClearDriver" )
-// }
-// else
-// {
-// entity oldOwner = expect entity( turret.GetOwner() )
-// if ( oldOwner != null )
-// {
-// oldOwner.Signal( "ClearDriver" )
-// turret.ClearDriver()
-// }
-// turret.SetOwner( player )
-// turret.SetDriver( player )
-// thread ClearDriverOnDeath( turret, player )
-// }
-// //turret.SetUsePrompts( "DEACTIVATE", "DEACTIVATE" )
-// //turret.SetOwner( player )
-// //turret.SetBossPlayer( player )
-// //turret.SetUsableByGroup( "owner pilot" )
-//}
-//
-//function ClearDriverOnDeath( turret, player )
-//{
-// player.EndSignal( "ClearDriver" )
-// player.EndSignal( "OnDestroy" )
-// turret.EndSignal( "OnDestroy" )
-//
-// player.WaitSignal( "OnDeath" )
-//
-// if ( IsValid( turret ) )
-// turret.ClearDriver()
-//}
-////TODO: Handle death and handle deactivate.
-//
-// \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_ball_lightning.gnut b/Northstar.CustomServers/scripts/vscripts/weapons/_ball_lightning.gnut
deleted file mode 100644
index 9aae59e5..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_ball_lightning.gnut
+++ /dev/null
@@ -1,363 +0,0 @@
-untyped
-
-global function BallLightning_Init
-global function AttachBallLightning
-global function AttachBallLightningToProp
-global function CreateBallLightning
-global function DestroyBallLightningOnEnt
-global function GetBallLightningFromEnt
-
-global function RegisterBallLightningDamage
-
-global function BallLightningZapFX
-global function BallLightningZapTargets
-global function BallLightningZapConnectionFX
-
-struct {
- table< string, float > uniqueStrings
-} file
-
-function BallLightning_Init()
-{
- PrecacheParticleSystem( BALL_LIGHTNING_ZAP_FX )
-
- if ( BALL_LIGHTNING_FX_TABLE != "" )
- PrecacheImpactEffectTable( BALL_LIGHTNING_FX_TABLE )
-
- RegisterBallLightningDamage( eDamageSourceId.mp_weapon_arc_launcher )
- RegisterBallLightningDamage( eDamageSourceId.mp_titanweapon_arc_ball )
- RegisterBallLightningDamage( eDamageSourceId.mp_weapon_arc_trap )
-}
-
-function AttachBallLightning( entity weapon, entity projectile )
-{
- Assert( !( "ballLightning" in projectile.s ) )
-
- int damageSourceId
- entity owner
-
- if ( weapon.IsProjectile() )
- {
- owner = weapon.GetOwner()
- damageSourceId = weapon.ProjectileGetDamageSourceID()
- }
- else
- {
- owner = weapon.GetWeaponOwner()
- damageSourceId = weapon.GetDamageSourceID()
- }
-
-
- entity ball = CreateBallLightning( owner, damageSourceId, projectile.GetOrigin(), projectile.GetAngles() )
- ball.SetParent( projectile )
- projectile.s.ballLightning <- ball
-}
-
-void function DestroyBallLightningOnEnt( entity prop )
-{
- if ( "ballLightning" in prop.s )
- {
- prop.s.ballLightning.Destroy()
- delete prop.s.ballLightning
- }
-}
-
-
-entity function AttachBallLightningToProp( entity prop, entity owner, int damageSourceId )
-{
- entity ball = CreateBallLightning( owner, damageSourceId, prop.GetOrigin(), prop.GetAngles() )
- ball.SetParent( prop )
- prop.s.ballLightning <- ball
- return ball
-}
-
-entity function CreateBallLightning( entity owner, int damageSourceId, vector origin, vector angles )
-{
- entity ballLightning = CreateScriptMover( origin, angles )
- ballLightning.SetOwner( owner )
- SetTeam( ballLightning, owner.GetTeam() )
-
- thread BallLightningThink( ballLightning, damageSourceId )
- return ballLightning
-}
-
-void function RegisterBallLightningDamage( int damageSourceId )
-{
- AddDamageCallbackSourceID( damageSourceId, OnBallLightningDamage )
-}
-
-void function OnBallLightningDamage( entity victim, var damageInfo )
-{
- float damage = DamageInfo_GetDamage( damageInfo )
-
- if ( damage <= 0 )
- return
-
- if ( victim.IsWorld() )
- return
-
- if ( victim.IsProjectile() )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & (DF_EXPLOSION | DF_IMPACT) )
- return
-
- // if ( IsHumanSized( victim ) )
- // {
- // DamageInfo_SetDamage( damageInfo, 0 )
- // return
- // }
-
- entity ballLightning = DamageInfo_GetInflictor( damageInfo )
-
- if ( victim == ballLightning )
- return
-
- if ( victim.GetParent() == ballLightning )
- return
-
- if ( !IsTargetEntValid( ballLightning, victim, ballLightning.e.ballLightningData ) )
- {
- DamageInfo_SetDamage( damageInfo, 0 )
- return
- }
-
- vector origin = DamageInfo_GetDamagePosition( damageInfo )
- int hitBox = DamageInfo_GetHitBox( damageInfo )
-
- string tag = GetEntityCenterTag( victim )
- thread BallLightningZapConnectionFX( ballLightning, victim, tag, ballLightning.e.ballLightningData )
- BallLightningZapFX( ballLightning, victim, tag, ballLightning.e.ballLightningData )
-}
-
-void function BallLightningThink( entity ballLightning, int damageSourceId )
-{
- ballLightning.EndSignal( "OnDestroy" )
-
- EmitSoundOnEntity( ballLightning, "Weapon_Arc_Ball_Loop" )
-
- local data = {}
-
- OnThreadEnd(
- function() : ( ballLightning, data )
- {
- if ( IsValid( ballLightning ) )
- StopSoundOnEntity( ballLightning, "Weapon_Arc_Ball_Loop" )
- }
- )
-
- int inflictorTeam = ballLightning.GetTeam()
- ballLightning.e.ballLightningTargetsIdx = CreateScriptManagedEntArray()
-
- WaitEndFrame()
-
- while( 1 )
- {
- for( int i=0; i<BALL_LIGHTNING_BURST_NUM; i++ )
- {
- if ( BALL_LIGHTNING_BURST_NUM > 1 )
- wait BALL_LIGHTNING_BURST_PAUSE
-
- vector origin = ballLightning.GetOrigin()
- BallLightningZapTargets( ballLightning, origin, inflictorTeam, damageSourceId, ballLightning.e.ballLightningData, false )
- }
- wait BALL_LIGHTNING_BURST_DELAY
- }
-}
-
-void function BallLightningZapTargets( entity ballLightning, vector origin, int inflictorTeam, int damageSourceId, BallLightningData fxData, bool single )
-{
- RadiusDamage(
- origin, // origin
- ballLightning.GetOwner(), // owner
- ballLightning, // inflictor
- fxData.damageToPilots, // normal damage
- fxData.damage, // heavy armor damage
- fxData.radius, // inner radius
- fxData.radius, // outer radius
- SF_ENVEXPLOSION_NO_DAMAGEOWNER, // explosion flags
- 0, // distanceFromAttacker
- 0, // explosionForce
- fxData.deathPackage, // damage flags
- damageSourceId // damage source id
- )
-}
-
-string function GetEntityCenterTag( entity target )
-{
- string tag = "center"
-
- if ( IsHumanSized( target ) )
- tag = "CHESTFOCUS"
- else if ( target.IsTitan() )
- tag = "HIJACK"
- else if ( IsSuperSpectre( target ) || IsAirDrone( target ) )
- tag = "CHESTFOCUS"
- else if ( IsDropship( target ) )
- tag = "ORIGIN"
- else if ( target.GetClassName() == "npc_turret_mega" )
- tag = "ATTACH"
-
- return tag
-}
-
-bool function IsTargetEntValid( entity ballLightning, entity target, BallLightningData fxData )
-{
- if ( !IsValid( target ) )
- return false
-
- vector origin = ballLightning.GetOrigin()
-
- if ( target == ballLightning )
- return false
-
- if ( target == ballLightning.GetParent() )
- return false
-
- if ( target.GetParent() == ballLightning.GetParent() )
- return false
-
- // if ( target.IsPlayer() && !target.IsTitan() )
- // return false
-
- if ( fabs( origin.z - target.GetOrigin().z ) > fxData.height )
- return false
-
- if ( GetBugReproNum() != 131703 )
- {
- if ( target.GetModelName() == $"" )
- return false
- }
-
- if ( !( target.GetClassName() in ArcCannonTargetClassnames ) )
- return false
-
- vector entityCenter = target.GetWorldSpaceCenter()
-
- if ( target.GetModelName() != $"" )
- {
- string tag = GetEntityCenterTag( target )
- int index = target.LookupAttachment( tag )
-
- if ( index == 0 )
- return false
-
- entityCenter = target.GetAttachmentOrigin( index )
- }
-
- vector fwd = AnglesToForward( ballLightning.GetAngles() )
- vector fwdToEnemy = Normalize( entityCenter - ballLightning.GetOrigin() )
-
- float dot = DotProduct( fwd, fwdToEnemy )
-
- if ( dot < fxData.minDot )
- return false
-
-
- if ( IsHumanSized( target ) )
- {
- float maxDist = fxData.humanRadius
- if ( Distance( entityCenter, ballLightning.GetOrigin() ) > maxDist )
- return false
- }
-
- // array<entity> ignoreEnts = [ target, ballLightning ]
- // if ( ballLightning.GetParent() != null )
- // ignoreEnts.append( ballLightning.GetParent() )
-
- // TraceResults trace = TraceLine( ballLightning.GetOrigin(), entityCenter, ignoreEnts, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_BLOCK_WEAPONS )
-
- // if ( trace.fraction < 1 )
- // return false
-
- VortexBulletHit ornull vortexHit = VortexBulletHitCheck( ballLightning.GetOwner(), ballLightning.GetOrigin(), entityCenter )
-
- if ( vortexHit )
- return false
-
- return true
-}
-
-void function BallLightningZapConnectionFX( entity ballLightning, entity target, string tag, BallLightningData fxData )
-{
- if ( fxData.zapFx != $"" )
- {
- // Control point sets the end position of the effect
- entity cpEnd = CreateEntity( "info_placement_helper" )
- SetTargetName( cpEnd, GetUniqueCpString() )
- cpEnd.SetParent( target, tag, false, 0.0 )
- DispatchSpawn( cpEnd )
-
- entity zapBeam = CreateEntity( "info_particle_system" )
- zapBeam.kv.cpoint1 = cpEnd.GetTargetName()
-
- zapBeam.SetValueForEffectNameKey( fxData.zapFx )
- zapBeam.kv.start_active = 0
- zapBeam.SetOwner( ballLightning )
- zapBeam.kv.VisibilityFlags = ENTITY_VISIBLE_TO_EVERYONE
- zapBeam.SetParent( ballLightning, "", false, 0.0 )
- DispatchSpawn( zapBeam )
-
- zapBeam.Fire( "Start" )
-
- OnThreadEnd(
- function() : ( zapBeam, cpEnd )
- {
- if ( IsValid( zapBeam ) )
- zapBeam.Destroy()
- if ( IsValid( cpEnd ) )
- cpEnd.Destroy()
- }
- )
-
- ballLightning.EndSignal( "OnDestroy" )
- target.EndSignal( "OnDestroy" )
- target.EndSignal( "OnDeath" )
-
- if ( fxData.zapLifetime > 0 )
- {
- wait fxData.zapLifetime
- }
- }
-}
-
-void function BallLightningZapFX( entity ballLightning, entity target, string tag, BallLightningData fxData )
-{
- int index = target.LookupAttachment( tag )
-
- vector entityCenter = target.GetAttachmentOrigin( index )
-
- if ( fxData.zapImpactTable != "" )
- PlayImpactFXTable( entityCenter, ballLightning.GetOwner(), fxData.zapImpactTable, SF_ENVEXPLOSION_INCLUDE_ENTITIES )
-
- EmitSoundOnEntity( ballLightning, fxData.zapSound )
- thread FadeOutSoundOnEntityAfterDelay( ballLightning, fxData.zapSound, 0.2, 0.2 )
-}
-
-// This is to minimize creation of new Unique Strings
-string function GetUniqueCpString()
-{
- foreach ( string uString, float useTime in file.uniqueStrings )
- {
- if ( useTime + BALL_LIGHTNING_ZAP_LIFETIME*2 > Time() )
- continue
-
- file.uniqueStrings[ uString ] = Time()
- return uString
- }
-
- string newString = UniqueString( "ball_lightning_cpEnd" )
-
- // printt( "Generated new string " + newString )
-
- file.uniqueStrings[ newString ] <- Time()
- return newString
-}
-
-entity function GetBallLightningFromEnt( entity ent )
-{
- if ( "ballLightning" in ent.s )
- return expect entity( ent.s.ballLightning )
-
- return null
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_cloaker.gnut b/Northstar.CustomServers/scripts/vscripts/weapons/_cloaker.gnut
deleted file mode 100644
index 6ec0bc0a..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_cloaker.gnut
+++ /dev/null
@@ -1,121 +0,0 @@
-untyped
-
-global function CloakerThink
-global function CloakerShouldCloakGuy
-
-global function CloakerCloaksGuy
-global function CloakerDeCloaksGuy
-
-function CloakerThink( entity cloaker, float radius, array<string> ents = [ "any" ], vector offset = Vector(0,0,0), var shouldCloakGuyFunc = null, float waitTime = 0.25 )
-{
- OnThreadEnd(
- function() : ( cloaker )
- {
- local cloakList = clone cloaker.s.cloakList
- foreach ( entity guy, value in cloakList )
- {
- if ( !IsAlive( guy ) )
- continue
-
- CloakerDeCloaksGuy( guy )
- }
- }
- )
-
- cloaker.s.cloakList <- {}
- cloaker.s.decloakList <- {}
-
- while( 1 )
- {
- vector origin = cloaker.GetOrigin() + offset
- array<entity> guys
-
- foreach ( entType in ents )
- {
- switch ( entType )
- {
- case "player":
- case "players":
- guys.extend( GetPlayerArrayEx( "any", cloaker.GetTeam(), TEAM_ANY, origin, radius ) )
- break;
- default:
- guys.extend( GetNPCArrayEx( entType, cloaker.GetTeam(), TEAM_ANY, origin, radius ) )
- break
- }
- }
- int index = 0
-
- float startTime = Time()
-
- table cloakList = expect table( cloaker.s.cloakList )
- cloaker.s.decloakList = clone cloakList
-
- foreach ( guy in guys )
- {
- //only do 5 distanceSqr / cansee checks per frame
- if ( index++ > 5 )
- {
- wait 0.1
- index = 0
- origin = cloaker.GetOrigin() + offset
- }
-
- bool shouldCloakGuy = CloakerShouldCloakGuy( cloaker, guy )
-
- if ( shouldCloakGuy )
- shouldCloakGuy = expect bool( shouldCloakGuyFunc( cloaker, guy ) )
-
- if ( shouldCloakGuy )
- {
- if ( guy in cloaker.s.decloakList )
- delete cloaker.s.decloakList[ guy ]
-
- if ( IsCloaked( guy ) )
- continue
-
- cloakList[ guy ] <- true
- CloakerCloaksGuy( guy )
- }
- }
-
- foreach ( entity guy, value in cloaker.s.decloakList )
- {
- // any guys still in the decloakList shouldn't be decloaked ... if alive.
- Assert( guy in cloakList )
- delete cloakList[ guy ]
-
- if ( IsAlive( guy ) )
- CloakerDeCloaksGuy( guy )
- }
-
- float endTime = Time()
- float elapsedTime = endTime - startTime
- if ( elapsedTime < waitTime )
- wait waitTime - elapsedTime
- }
-}
-
-void function CloakerCloaksGuy( guy )
-{
- guy.SetCloakDuration( 2.0, -1, 0 )
- EmitSoundOnEntity( guy, CLOAKED_DRONE_CLOAK_START_SFX )
- EmitSoundOnEntity( guy, CLOAKED_DRONE_CLOAK_LOOP_SFX )
- guy.Minimap_Hide( TEAM_IMC, null )
- guy.Minimap_Hide( TEAM_MILITIA, null )
-}
-
-void function CloakerDeCloaksGuy( guy )
-{
- guy.SetCloakDuration( 0, 0, 1.5 )
- StopSoundOnEntity( guy, CLOAKED_DRONE_CLOAK_LOOP_SFX )
- guy.Minimap_AlwaysShow( TEAM_IMC, null )
- guy.Minimap_AlwaysShow( TEAM_MILITIA, null )
-}
-
-bool function CloakerShouldCloakGuy( entity cloaker, entity guy )
-{
- if ( !IsAlive( guy ) )
- return false
-
- return true
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_grenade.nut b/Northstar.CustomServers/scripts/vscripts/weapons/_grenade.nut
deleted file mode 100644
index c2036e85..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_grenade.nut
+++ /dev/null
@@ -1,604 +0,0 @@
-untyped
-
-global function Grenade_FileInit
-global function GetGrenadeThrowSound_1p
-global function GetGrenadeDeploySound_1p
-global function GetGrenadeThrowSound_3p
-global function GetGrenadeDeploySound_3p
-global function GetGrenadeProjectileSound
-
-const DEFAULT_FUSE_TIME = 2.25
-const DEFAULT_WARNING_TIME = 1.0
-global const float DEFAULT_MAX_COOK_TIME = 99999.9 //Longer than an entire day. Really just an arbitrarily large number
-
-global function Grenade_OnWeaponTossReleaseAnimEvent
-global function Grenade_OnWeaponTossCancelDrop
-global function Grenade_OnWeaponDeactivate
-global function Grenade_OnWeaponTossPrep
-global function Grenade_OnProjectileIgnite
-
-#if SERVER
- global function Grenade_OnPlayerNPCTossGrenade_Common
- global function ProxMine_Triggered
- global function EnableTrapWarningSound
- global function AddToProximityTargets
- global function ProximityMineThink
-#endif
-global function Grenade_Init
-
-const GRENADE_EXPLOSIVE_WARNING_SFX_LOOP = "Weapon_Vortex_Gun.ExplosiveWarningBeep"
-const EMP_MAGNETIC_FORCE = 1600
-const MAG_FLIGHT_SFX_LOOP = "Explo_MGL_MagneticAttract"
-
-//Proximity Mine Settings
-global const PROXIMITY_MINE_EXPLOSION_DELAY = 1.2
-global const PROXIMITY_MINE_ARMING_DELAY = 1.0
-const TRIGGERED_ALARM_SFX = "Weapon_ProximityMine_CloseWarning"
-global const THERMITE_GRENADE_FX = $"P_grenade_thermite"
-global const CLUSTER_BASE_FX = $"P_wpn_meteor_exp"
-
-global const ProximityTargetClassnames = {
- [ "npc_soldier_shield" ] = true,
- [ "npc_soldier_heavy" ] = true,
- [ "npc_soldier" ] = true,
- [ "npc_spectre" ] = true,
- [ "npc_drone" ] = true,
- [ "npc_titan" ] = true,
- [ "npc_marvin" ] = true,
- [ "player" ] = true,
- [ "npc_turret_mega" ] = true,
- [ "npc_turret_sentry" ] = true,
- [ "npc_dropship" ] = true,
-}
-
-const SOLDIER_ARC_STUN_ANIMS = [
- "pt_react_ARC_fall",
- "pt_react_ARC_kneefall",
- "pt_react_ARC_sidefall",
- "pt_react_ARC_slowfall",
- "pt_react_ARC_scream",
- "pt_react_ARC_stumble_F",
- "pt_react_ARC_stumble_R" ]
-
-function Grenade_FileInit()
-{
- PrecacheParticleSystem( CLUSTER_BASE_FX )
-
- RegisterSignal( "ThrowGrenade" )
- RegisterSignal( "WeaponDeactivateEvent" )
- RegisterSignal( "OnEMPPilotHit" )
- RegisterSignal( "StopGrenadeClientEffects" )
- RegisterSignal( "DisableTrapWarningSound" )
-
- //Globalize( MagneticFlight )
-
- #if CLIENT
- AddDestroyCallback( "grenade_frag", ClientDestroyCallback_GrenadeDestroyed )
- #endif
-
- #if SERVER
- level._empForcedCallbacks <- {}
- level._proximityTargetArrayID <- CreateScriptManagedEntArray()
-
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_proximity_mine, ProxMine_Triggered )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_thermite_grenade, Thermite_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_frag_grenade, Frag_DamagedPlayerOrNPC )
-
- level._empForcedCallbacks[eDamageSourceId.mp_weapon_grenade_emp] <- true
- level._empForcedCallbacks[eDamageSourceId.mp_weapon_proximity_mine] <- true
-
- PrecacheParticleSystem( THERMITE_GRENADE_FX )
- #endif
-}
-
-void function Grenade_OnWeaponTossPrep( entity weapon, WeaponTossPrepParams prepParams )
-{
- weapon.w.startChargeTime = Time()
-
- entity weaponOwner = weapon.GetWeaponOwner()
- weapon.EmitWeaponSound_1p3p( GetGrenadeDeploySound_1p( weapon ), GetGrenadeDeploySound_3p( weapon ) )
-
- #if SERVER
- thread HACK_CookGrenade( weapon, weaponOwner )
- thread HACK_DropGrenadeOnDeath( weapon, weaponOwner )
- #elseif CLIENT
- if ( weaponOwner.IsPlayer() )
- {
- weaponOwner.p.grenadePulloutTime = Time()
- }
- #endif
-}
-
-void function Grenade_OnWeaponDeactivate( entity weapon )
-{
- StopSoundOnEntity( weapon, GRENADE_EXPLOSIVE_WARNING_SFX_LOOP )
- weapon.Signal( "WeaponDeactivateEvent" )
-}
-
-void function Grenade_OnProjectileIgnite( entity weapon )
-{
- printt( "Grenade_OnProjectileIgnite() callback." )
-}
-
-function Grenade_Init( entity grenade, entity weapon )
-{
- entity weaponOwner = weapon.GetOwner()
- if ( IsValid( weaponOwner ) )
- SetTeam( grenade, weaponOwner.GetTeam() )
-
- // JFS: this is because I don't know if the above line should be
- // weapon.GetOwner() or it's a typo and should really be weapon.GetWeaponOwner()
- // and it's too close to ship and who knows what effect that will have
- entity owner = weapon.GetWeaponOwner()
- if ( IsMultiplayer() && IsValid( owner ) )
- {
- if ( owner.IsNPC() )
- {
- SetTeam( grenade, owner.GetTeam() )
- }
- }
-
- #if SERVER
- bool smartPistolVisible = weapon.GetWeaponSettingBool( eWeaponVar.projectile_visible_to_smart_ammo )
- if ( smartPistolVisible )
- {
- grenade.SetDamageNotifications( true )
- grenade.SetTakeDamageType( DAMAGE_EVENTS_ONLY )
- grenade.proj.onlyAllowSmartPistolDamage = true
-
- if ( !grenade.GetProjectileWeaponSettingBool( eWeaponVar.projectile_damages_owner ) && !grenade.GetProjectileWeaponSettingBool( eWeaponVar.explosion_damages_owner ) )
- SetCustomSmartAmmoTarget( grenade, true ) // prevent friendly target lockon
- }
- else
- {
- grenade.SetTakeDamageType( DAMAGE_NO )
- }
- #endif
- if ( IsValid( weaponOwner ) )
- grenade.s.originalOwner <- weaponOwner // for later in damage callbacks, to skip damage vs friendlies but not for og owner or his enemies
-}
-
-
-int function Grenade_OnWeaponToss_( entity weapon, WeaponPrimaryAttackParams attackParams, float directionScale )
-{
- weapon.EmitWeaponSound_1p3p( GetGrenadeThrowSound_1p( weapon ), GetGrenadeThrowSound_3p( weapon ) )
- bool projectilePredicted = PROJECTILE_PREDICTED
- bool projectileLagCompensated = PROJECTILE_LAG_COMPENSATED
-#if SERVER
- if ( weapon.IsForceReleaseFromServer() )
- {
- projectilePredicted = false
- projectileLagCompensated = false
- }
-#endif
- entity grenade = Grenade_Launch( weapon, attackParams.pos, (attackParams.dir * directionScale), projectilePredicted, projectileLagCompensated )
- entity weaponOwner = weapon.GetWeaponOwner()
- weaponOwner.Signal( "ThrowGrenade" )
-
- PlayerUsedOffhand( weaponOwner, weapon ) // intentionally here and in Hack_DropGrenadeOnDeath - accurate for when cooldown actually begins
-
-#if SERVER
-
- #if BATTLECHATTER_ENABLED
- TryPlayWeaponBattleChatterLine( weaponOwner, weapon )
- #endif
-
-#endif
-
- return weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot )
-}
-
-var function Grenade_OnWeaponTossReleaseAnimEvent( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- var result = Grenade_OnWeaponToss_( weapon, attackParams, 1.0 )
- return result
-}
-
-var function Grenade_OnWeaponTossCancelDrop( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- var result = Grenade_OnWeaponToss_( weapon, attackParams, 0.2 )
- return result
-}
-
-// Can return entity or nothing
-entity function Grenade_Launch( entity weapon, vector attackPos, vector throwVelocity, bool isPredicted, bool isLagCompensated )
-{
- #if CLIENT
- if ( !weapon.ShouldPredictProjectiles() )
- return null
- #endif
-
- //TEMP FIX while Deploy anim is added to sprint
- float currentTime = Time()
- if ( weapon.w.startChargeTime == 0.0 )
- weapon.w.startChargeTime = currentTime
-
- entity weaponOwner = weapon.GetWeaponOwner()
-
- var discThrow = weapon.GetWeaponInfoFileKeyField( "grenade_disc_throw" )
-
- vector angularVelocity = Vector( 3600, RandomFloatRange( -1200, 1200 ), 0 )
-
- if ( discThrow == 1 )
- angularVelocity = Vector( 100, 100, RandomFloatRange( 1200, 2200 ) )
-
-
- float fuseTime
-
- float baseFuseTime = weapon.GetGrenadeFuseTime() //Note that fuse time of 0 means the grenade won't explode on its own, instead it depends on OnProjectileCollision() functions to be defined and explode there. Arguably in this case grenade_fuse_time shouldn't be 0, but an arbitrarily large number instead.
- if ( baseFuseTime > 0.0 )
- {
- fuseTime = baseFuseTime - ( currentTime - weapon.w.startChargeTime )
- if ( fuseTime <= 0 )
- fuseTime = 0.001
- }
- else
- {
- fuseTime = baseFuseTime
- }
-
- int damageFlags = weapon.GetWeaponDamageFlags()
- entity frag = weapon.FireWeaponGrenade( attackPos, throwVelocity, angularVelocity, fuseTime, damageFlags, damageFlags, isPredicted, isLagCompensated, true )
- if ( frag == null )
- return null
-
- if ( discThrow == 1 ) // add wobble by pitching it slightly
- {
- Assert( !frag.IsMarkedForDeletion(), "Frag before .SetAngles() is marked for deletion." )
- frag.SetAngles( frag.GetAngles() + < RandomFloatRange( 7,11 ),0,0 > )
- //Assert( !frag.IsMarkedForDeletion(), "Frag after .SetAngles() is marked for deletion." )
- if ( frag.IsMarkedForDeletion() )
- {
- CodeWarning( "Frag after .SetAngles() was marked for deletion." )
- return null
- }
- }
-
- Grenade_OnPlayerNPCTossGrenade_Common( weapon, frag )
-
- return frag
-}
-
-void function Grenade_OnPlayerNPCTossGrenade_Common( entity weapon, entity frag )
-{
- Grenade_Init( frag, weapon )
- #if SERVER
- thread TrapExplodeOnDamage( frag, 20, 0.0, 0.0 )
-
- string projectileSound = GetGrenadeProjectileSound( weapon )
- if ( projectileSound != "" )
- EmitSoundOnEntity( frag, projectileSound )
- #endif
-
- if( weapon.HasMod( "burn_mod_emp_grenade" ) )
- frag.InitMagnetic( EMP_MAGNETIC_FORCE, MAG_FLIGHT_SFX_LOOP )
-}
-
-struct CookGrenadeStruct //Really just a convenience struct so we can read the changed value of a bool in an OnThreadEnd
-{
- bool shouldOverrideFuseTime = false
-}
-
-void function HACK_CookGrenade( entity weapon, entity weaponOwner )
-{
- float maxCookTime = GetMaxCookTime( weapon )
- if ( maxCookTime >= DEFAULT_MAX_COOK_TIME )
- return
-
- weaponOwner.EndSignal( "OnDeath" )
- weaponOwner.EndSignal( "ThrowGrenade" )
- weapon.EndSignal( "WeaponDeactivateEvent" )
- weapon.EndSignal( "OnDestroy" )
-
- /*CookGrenadeStruct grenadeStruct
-
- OnThreadEnd(
- function() : ( weapon, grenadeStruct )
- {
- if ( grenadeStruct.shouldOverrideFuseTime )
- {
- var minFuseTime = weapon.GetWeaponInfoFileKeyField( "min_fuse_time" )
- printt( "minFuseTime: " + minFuseTime )
- if ( minFuseTime != null )
- {
- expect float( minFuseTime )
- printt( "Setting overrideFuseTime to : " + weapon.GetWeaponInfoFileKeyField( "min_fuse_time" ) )
- weapon.w.overrideFuseTime = minFuseTime
- }
- }
- }
- )
-*/
- if ( maxCookTime - DEFAULT_WARNING_TIME <= 0 )
- {
- EmitSoundOnEntity( weapon, GRENADE_EXPLOSIVE_WARNING_SFX_LOOP )
- wait maxCookTime
- }
- else
- {
- wait( maxCookTime - DEFAULT_WARNING_TIME )
-
- EmitSoundOnEntity( weapon, GRENADE_EXPLOSIVE_WARNING_SFX_LOOP )
-
- wait( DEFAULT_WARNING_TIME )
- }
-
- if ( !IsValid( weapon.GetWeaponOwner() ) )
- return
-
- weapon.ForceReleaseFromServer() // Will eventually result in Grenade_OnWeaponToss_() or equivalent function
-
- // JFS: prevent grenade cook exploit in coliseum
- if ( GameRules_GetGameMode() == COLISEUM )
- {
- #if SERVER
- int damageSource = weapon.GetDamageSourceID()
-
- if ( damageSource == eDamageSourceId.mp_weapon_frag_grenade )
- {
- var impact_effect_table = weapon.GetWeaponInfoFileKeyField( "impact_effect_table" )
- if ( impact_effect_table != null )
- {
- string fx = expect string( impact_effect_table )
- PlayImpactFXTable( weaponOwner.EyePosition(), weaponOwner, fx )
- }
- weaponOwner.Die( weaponOwner, weapon, { damageSourceId = damageSource } )
- }
- #endif
- }
-
- weaponOwner.Signal( "ThrowGrenade" ) // Only necessary to end HACK_DropGrenadeOnDeath
-}
-
-
-void function HACK_WaitForGrenadeDropEvent( weapon, entity weaponOwner )
-{
- weapon.EndSignal( "WeaponDeactivateEvent" )
-
- weaponOwner.WaitSignal( "OnDeath" )
-}
-
-
-void function HACK_DropGrenadeOnDeath( entity weapon, entity weaponOwner )
-{
- if ( weapon.HasMod( "burn_card_weapon_mod" ) ) //JFS: Primarily to stop boost grenade weapons (e.g. frag_drone ) not doing TryUsingBurnCardWeapon() when dropped through this function.
- return
-
- weaponOwner.EndSignal( "ThrowGrenade" )
- weaponOwner.EndSignal( "OnDestroy" )
-
- waitthread HACK_WaitForGrenadeDropEvent( weapon, weaponOwner )
-
- if( !IsValid( weaponOwner ) || !IsValid( weapon ) || IsAlive( weaponOwner ) )
- return
-
- float elapsedTime = Time() - weapon.w.startChargeTime
- float baseFuseTime = weapon.GetGrenadeFuseTime()
- float fuseDelta = (baseFuseTime - elapsedTime)
-
- if ( (baseFuseTime == 0.0) || (fuseDelta > -0.1) )
- {
- float forwardScale = weapon.GetWeaponSettingFloat( eWeaponVar.grenade_death_drop_velocity_scale )
- vector velocity = weaponOwner.GetForwardVector() * forwardScale
- velocity.z += weapon.GetWeaponSettingFloat( eWeaponVar.grenade_death_drop_velocity_extraUp )
- vector angularVelocity = Vector( 0, 0, 0 )
- float fuseTime = baseFuseTime ? baseFuseTime - elapsedTime : baseFuseTime
-
- int primaryClipCount = weapon.GetWeaponPrimaryClipCount()
- int ammoPerShot = weapon.GetWeaponSettingInt( eWeaponVar.ammo_per_shot )
- weapon.SetWeaponPrimaryClipCountAbsolute( maxint( 0, primaryClipCount - ammoPerShot ) )
-
- PlayerUsedOffhand( weaponOwner, weapon ) // intentionally here and in ReleaseAnimEvent - for cases where grenade is dropped on death
-
- entity grenade = Grenade_Launch( weapon, weaponOwner.GetOrigin(), velocity, PROJECTILE_NOT_PREDICTED, PROJECTILE_NOT_LAG_COMPENSATED )
- }
-}
-
-
-#if SERVER
-void function ProxMine_Triggered( entity ent, var damageInfo )
-{
- if ( !IsValid( ent ) )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- entity attacker = DamageInfo_GetAttacker( damageInfo )
-
- if ( !IsValid( attacker ) )
- return
-
- if ( attacker == ent )
- return
-
- if ( ent.IsPlayer() || ent.IsNPC() )
- thread ShowProxMineTriggeredIcon( ent )
-
- //If this feature is good, we should add this to NPCs as well. Currently script errors if applied to an NPC.
- //if ( ent.IsPlayer() )
- // thread ProxMine_ShowOnMinimapTimed( ent, GetOtherTeam( ent.GetTeam() ), PROX_MINE_MARKER_TIME )
-}
-
-/*
-function ProxMine_ShowOnMinimapTimed( ent, teamToDisplayEntTo, duration )
-{
- ent.Minimap_AlwaysShow( teamToDisplayEntTo, null )
- Minimap_CreatePingForTeam( teamToDisplayEntTo, ent.GetOrigin(), $"vgui/HUD/titanFiringPing", 1.0 )
-
- wait duration
-
- if ( IsValid( ent ) && ent.IsPlayer() )
- ent.Minimap_DisplayDefault( teamToDisplayEntTo, ent )
-}
-*/
-
-void function Thermite_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- if ( !IsValid( ent ) )
- return
-
- Thermite_DamagePlayerOrNPCSounds( ent )
-}
-
-void function Frag_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- #if MP
- if ( !IsValid( ent ) || ent.IsPlayer() || ent.IsTitan() )
- return
-
- if ( ent.IsMechanical() )
- DamageInfo_ScaleDamage( damageInfo, 0.5 )
- #endif
-}
-
-#endif // SERVER
-
-
-#if CLIENT
-void function ClientDestroyCallback_GrenadeDestroyed( entity grenade )
-{
-}
-#endif // CLIENT
-
-#if SERVER
-function EnableTrapWarningSound( entity trap, delay = 0, warningSound = DEFAULT_WARNING_SFX )
-{
- trap.EndSignal( "OnDestroy" )
- trap.EndSignal( "DisableTrapWarningSound" )
-
- if ( delay > 0 )
- wait delay
-
- while ( IsValid( trap ) )
- {
- EmitSoundOnEntity( trap, warningSound )
- wait 1.0
- }
-}
-
-void function AddToProximityTargets( entity ent )
-{
- AddToScriptManagedEntArray( level._proximityTargetArrayID, ent );
-}
-
-function ProximityMineThink( entity proximityMine, entity owner )
-{
- proximityMine.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( proximityMine )
- {
- if ( IsValid( proximityMine ) )
- proximityMine.Destroy()
- }
- )
- thread TrapExplodeOnDamage( proximityMine, 50 )
-
- wait PROXIMITY_MINE_ARMING_DELAY
-
- int teamNum = proximityMine.GetTeam()
- float explodeRadius = proximityMine.GetDamageRadius()
- float triggerRadius = ( ( explodeRadius * 0.75 ) + 0.5 )
- local lastTimeNPCsChecked = 0
- local NPCTickRate = 0.5
- local PlayerTickRate = 0.2
-
- // Wait for someone to enter proximity
- while( IsValid( proximityMine ) && IsValid( owner ) )
- {
- if ( lastTimeNPCsChecked + NPCTickRate <= Time() )
- {
- array<entity> nearbyNPCs = GetNPCArrayEx( "any", TEAM_ANY, teamNum, proximityMine.GetOrigin(), triggerRadius )
- foreach( ent in nearbyNPCs )
- {
- if ( ShouldSetOffProximityMine( proximityMine, ent ) )
- {
- ProximityMine_Explode( proximityMine )
- return
- }
- }
- lastTimeNPCsChecked = Time()
- }
-
- array<entity> nearbyPlayers = GetPlayerArrayEx( "any", TEAM_ANY, teamNum, proximityMine.GetOrigin(), triggerRadius )
- foreach( ent in nearbyPlayers )
- {
- if ( ShouldSetOffProximityMine( proximityMine, ent ) )
- {
- ProximityMine_Explode( proximityMine )
- return
- }
- }
-
- wait PlayerTickRate
- }
-}
-
-function ProximityMine_Explode( proximityMine )
-{
- local explodeTime = Time() + PROXIMITY_MINE_EXPLOSION_DELAY
- EmitSoundOnEntity( proximityMine, TRIGGERED_ALARM_SFX )
-
- wait PROXIMITY_MINE_EXPLOSION_DELAY
-
- if ( IsValid( proximityMine ) )
- proximityMine.GrenadeExplode( proximityMine.GetForwardVector() )
-}
-
-bool function ShouldSetOffProximityMine( entity proximityMine, entity ent )
-{
- if ( !IsAlive( ent ) )
- return false
-
- if ( ent.IsPhaseShifted() )
- return false
-
- TraceResults results = TraceLine( proximityMine.GetOrigin(), ent.EyePosition(), proximityMine, (TRACE_MASK_SHOT | CONTENTS_BLOCKLOS), TRACE_COLLISION_GROUP_NONE )
- if ( results.fraction >= 1 || results.hitEnt == ent )
- return true
-
- return false
-}
-
-#endif // SERVER
-
-
-
-float function GetMaxCookTime( entity weapon )
-{
- var cookTime = weapon.GetWeaponInfoFileKeyField( "max_cook_time" )
- if (cookTime == null )
- return DEFAULT_MAX_COOK_TIME
-
- expect float ( cookTime )
- return cookTime
-}
-
-function GetGrenadeThrowSound_1p( weapon )
-{
- return weapon.GetWeaponInfoFileKeyField( "sound_throw_1p" ) ? weapon.GetWeaponInfoFileKeyField( "sound_throw_1p" ) : ""
-}
-
-
-function GetGrenadeDeploySound_1p( weapon )
-{
- return weapon.GetWeaponInfoFileKeyField( "sound_deploy_1p" ) ? weapon.GetWeaponInfoFileKeyField( "sound_deploy_1p" ) : ""
-}
-
-
-function GetGrenadeThrowSound_3p( weapon )
-{
- return weapon.GetWeaponInfoFileKeyField( "sound_throw_3p" ) ? weapon.GetWeaponInfoFileKeyField( "sound_throw_3p" ) : ""
-}
-
-
-function GetGrenadeDeploySound_3p( weapon )
-{
- return weapon.GetWeaponInfoFileKeyField( "sound_deploy_3p" ) ? weapon.GetWeaponInfoFileKeyField( "sound_deploy_3p" ) : ""
-}
-
-string function GetGrenadeProjectileSound( weapon )
-{
- return expect string( weapon.GetWeaponInfoFileKeyField( "sound_grenade_projectile" ) ? weapon.GetWeaponInfoFileKeyField( "sound_grenade_projectile" ) : "" )
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_particle_wall.gnut b/Northstar.CustomServers/scripts/vscripts/weapons/_particle_wall.gnut
deleted file mode 100644
index a46bfff8..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_particle_wall.gnut
+++ /dev/null
@@ -1,460 +0,0 @@
-untyped
-
-global function ParticleWall_Init
-
-global function CreateTurretParticleWall
-global function CreateParticleWallFromOwner
-global function CreateShieldWithSettings
-global function DrainHealthOverTime
-
-global function CreateAmpedWallFromOwner
-
-global function CreateParticleWallForOwnerFromDirection
-
-global const SHIELD_WALL_COL_MODEL = $"models/fx/xo_shield_wall.mdl"
-global const SHIELD_WALL_FX = $"P_xo_shield_wall"
-
-global const TURRET_SHIELD_WALL_COL_MODEL = $"models/fx/turret_shield_wall.mdl"
-global const TURRET_SHIELD_WALL_FX = $"P_turret_shield_wall"
-
-global const AMPED_WALL_FX = $"P_xo_amped_wall"
-#if MP
-global const SHIELD_WALL_HEALTH = 2000
-global const TURRET_SHIELD_WALL_HEALTH = 3500//1750
-#else
-global const SHIELD_WALL_HEALTH = 1750
-global const TURRET_SHIELD_WALL_HEALTH = 1750
-#endif
-global const PAS_TONE_WALL_HEALTH = 3000
-global const PAS_TONE_WALL_DURATION_MULTIPLIER = 1.5
-global const SHIELD_WALL_DURATION = 8.0
-global const SHIELD_WALL_RADIUS = 180
-global const SHIELD_WALL_FOV = 120
-global const SHIELD_WALL_WIDTH = 156.0 // SHIELD_WALL_RADIUS * cos( SHIELD_WALL_FOV/2 )
-
-global function UpdateShieldWallColorForFrac
-global function PlayEffectOnVortexSphere
-global function SetVortexSphereShieldWallCPoint
-global function SetShieldWallCPoint
-global function StopShieldWallFX
-global function StopShieldWallFXOverTime
-global function SetShieldWallCPointOrigin
-
-function ParticleWall_Init()
-{
- PrecacheParticleSystem( SHIELD_WALL_FX )
- PrecacheModel( SHIELD_WALL_COL_MODEL )
-
- PrecacheParticleSystem( TURRET_SHIELD_WALL_FX )
- PrecacheModel( TURRET_SHIELD_WALL_COL_MODEL )
-
- PrecacheParticleSystem( AMPED_WALL_FX )
-}
-
-void function CreateParticleWallFromOwner( entity weaponOwner, float duration, WeaponPrimaryAttackParams attackParams )
-{
- vector dir = GetParticleWallAttackAnglesFromOwner( weaponOwner, attackParams )
- CreateParticleWallForOwnerFromDirection( weaponOwner, duration, dir )
-}
-
-vector function GetParticleWallAttackAnglesFromOwner( entity weaponOwner, WeaponPrimaryAttackParams attackParams )
-{
- if ( weaponOwner.IsNPC() )
- return attackParams.dir
-
- vector angles = weaponOwner.CameraAngles()
- angles.x = 0
- return AnglesToForward( angles )
-}
-
-void function CreateParticleWallForOwnerFromDirection( entity weaponOwner, float duration, vector dir )
-{
- Assert( IsServer() )
-
- entity titanSoul = weaponOwner.GetTitanSoul()
-
- // JFS the weapon owner should always have a soul, at least on the server
- if ( !IsValid( titanSoul ) )
- return
-
- vector origin = weaponOwner.GetOrigin()
- vector safeSpot = origin
- vector angles = VectorToAngles( dir )
-
- if ( weaponOwner.IsNPC() )
- {
- // spawn in front of npc a bit
- origin += dir * 100
- }
-
- float endTime = Time() + duration
- titanSoul.SetDefensivePlacement( endTime, SHIELD_WALL_WIDTH, 0, true, safeSpot, dir )
-
- Assert( weaponOwner.IsTitan() )
- Assert( titanSoul )
-
- int health
- if ( SoulHasPassive( titanSoul, ePassives.PAS_TONE_WALL ) )
- {
- health = PAS_TONE_WALL_HEALTH
- duration *= PAS_TONE_WALL_DURATION_MULTIPLIER
- }
- else
- {
- health = SHIELD_WALL_HEALTH
- }
- entity vortexSphere = CreateShieldWithSettings( origin + < 0, 0, -64 >, angles, SHIELD_WALL_RADIUS, SHIELD_WALL_RADIUS * 2, SHIELD_WALL_FOV, duration, health, SHIELD_WALL_FX )
- thread DrainHealthOverTime( vortexSphere, vortexSphere.e.shieldWallFX, duration )
-
- entity groundEntity = weaponOwner.GetGroundEntity()
- if ( groundEntity != null && groundEntity.HasPusherRootParent() )
- vortexSphere.SetParent( groundEntity, "", true, 0 )
-}
-
-entity function CreateTurretParticleWall( vector origin, vector angles, float duration )
-{
- Assert( IsServer() )
-
- entity vortexSphere = CreateTurretShieldWithSettings( origin + < 0, 0, -64 >, angles, SHIELD_WALL_RADIUS, int( SHIELD_WALL_RADIUS * 1.65 ), 270, duration, TURRET_SHIELD_WALL_HEALTH, TURRET_SHIELD_WALL_FX )
- thread DrainHealthOverTime( vortexSphere, vortexSphere.e.shieldWallFX, duration )
-
- return vortexSphere
-}
-
-entity function CreateShieldWithSettings( vector origin, vector angles, int radius, int height, int fov, float duration, int health, asset effectName )
-{
- entity vortexSphere = CreateEntity( "vortex_sphere" )
-
- vortexSphere.kv.spawnflags = SF_ABSORB_BULLETS | SF_BLOCK_OWNER_WEAPON | SF_BLOCK_NPC_WEAPON_LOF | SF_ABSORB_CYLINDER
- vortexSphere.kv.enabled = 0
- vortexSphere.kv.radius = radius
- vortexSphere.kv.height = height
- vortexSphere.kv.bullet_fov = fov
- vortexSphere.kv.physics_pull_strength = 25
- vortexSphere.kv.physics_side_dampening = 6
- vortexSphere.kv.physics_fov = 360
- vortexSphere.kv.physics_max_mass = 2
- vortexSphere.kv.physics_max_size = 6
-
- vortexSphere.SetAngles( angles ) // viewvec?
- vortexSphere.SetOrigin( origin )
- vortexSphere.SetMaxHealth( health )
- vortexSphere.SetHealth( health )
- vortexSphere.SetTakeDamageType( DAMAGE_YES )
-
- DispatchSpawn( vortexSphere )
-
- vortexSphere.Fire( "Enable" )
- vortexSphere.Fire( "Kill", "", duration )
-
- // Shield wall fx control point
- entity cpoint = CreateEntity( "info_placement_helper" )
- SetTargetName( cpoint, UniqueString( "shield_wall_controlpoint" ) )
- DispatchSpawn( cpoint )
-
- // Shield wall fx
- entity shieldWallFX = PlayFXWithControlPoint( effectName, origin, cpoint, -1, null, angles, C_PLAYFX_LOOP )
- vortexSphere.e.shieldWallFX = shieldWallFX
- shieldWallFX.SetParent( vortexSphere )
- SetVortexSphereShieldWallCPoint( vortexSphere, cpoint )
- StopShieldWallFXOverTime( vortexSphere, duration )
-
-
- thread StopFXOnDestroy( vortexSphere, shieldWallFX, duration )
- return vortexSphere
-}
-
-//Turret Shields do not block npc line of fire.
-entity function CreateTurretShieldWithSettings( vector origin, vector angles, int radius, int height, int fov, float duration, int health, asset effectName )
-{
- entity vortexSphere = CreateEntity( "vortex_sphere" )
-
- vortexSphere.kv.spawnflags = SF_ABSORB_BULLETS | SF_BLOCK_OWNER_WEAPON | SF_ABSORB_CYLINDER
- vortexSphere.kv.enabled = 0
- vortexSphere.kv.radius = radius
- vortexSphere.kv.height = height
- vortexSphere.kv.bullet_fov = fov
- vortexSphere.kv.physics_pull_strength = 25
- vortexSphere.kv.physics_side_dampening = 6
- vortexSphere.kv.physics_fov = 360
- vortexSphere.kv.physics_max_mass = 2
- vortexSphere.kv.physics_max_size = 6
-
- vortexSphere.SetAngles( angles ) // viewvec?
- vortexSphere.SetOrigin( origin )
- vortexSphere.SetMaxHealth( health )
- vortexSphere.SetHealth( health )
- vortexSphere.SetTakeDamageType( DAMAGE_YES )
-
- DispatchSpawn( vortexSphere )
-
- vortexSphere.Fire( "Enable" )
- vortexSphere.Fire( "Kill", "", duration )
-
- // Shield wall fx control point
- entity cpoint = CreateEntity( "info_placement_helper" )
- SetTargetName( cpoint, UniqueString( "shield_wall_controlpoint" ) )
- DispatchSpawn( cpoint )
-
- // Shield wall fx
- entity shieldWallFX = PlayFXWithControlPoint( effectName, origin, cpoint, -1, null, angles, C_PLAYFX_LOOP )
- vortexSphere.e.shieldWallFX = shieldWallFX
- shieldWallFX.SetParent( vortexSphere )
- SetVortexSphereShieldWallCPoint( vortexSphere, cpoint )
- StopShieldWallFXOverTime( vortexSphere, duration )
-
-
- thread StopFXOnDestroy( vortexSphere, shieldWallFX, duration )
- return vortexSphere
-}
-
-function StopFXOnDestroy( entity vortexSphere, entity shieldWallFX, float duration )
-{
- vortexSphere.EndSignal( "OnDestroy" )
- shieldWallFX.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( vortexSphere )
- {
- StopShieldWallFX( vortexSphere )
- }
- )
-
- wait duration * 1.5
-}
-
-void function CreateAmpedWallFromOwner( entity weaponOwner, float duration, WeaponPrimaryAttackParams attackParams )
-{
- Assert( IsNewThread(), "Must be threaded off" )
- Assert( IsServer() )
- entity titanSoul = weaponOwner.GetTitanSoul()
-
- // JFS the weapon owner should always have a soul, at least on the server
- if ( !IsValid( titanSoul ) )
- return
- Assert( weaponOwner.IsTitan() )
-
- vector dir = GetParticleWallAttackAnglesFromOwner( weaponOwner, attackParams )
- vector origin = weaponOwner.GetOrigin()
- vector safeSpot = origin
- vector angles = VectorToAngles( dir )
- vector forward = AnglesToForward( angles )
- angles = AnglesCompose( angles, <0,180,0> )
-
- if ( weaponOwner.IsNPC() )
- {
- // spawn in front of npc a bit
- origin += dir * 100
- }
-
- origin += dir * 500
- origin += Vector(0,0,-64)
-
- float endTime = Time() + duration
- titanSoul.SetDefensivePlacement( endTime, SHIELD_WALL_WIDTH, 0, true, safeSpot, dir )
-
- entity vortexSphere = CreateShieldWithSettings( origin, angles, SHIELD_WALL_RADIUS, SHIELD_WALL_RADIUS * 2, SHIELD_WALL_FOV, duration, SHIELD_WALL_HEALTH, AMPED_WALL_FX )
- vortexSphere.EndSignal( "OnDestroy" )
- entity shieldWallFX = vortexSphere.e.shieldWallFX
- shieldWallFX.EndSignal( "OnDestroy" )
- SetTargetName( vortexSphere, PROTO_AMPED_WALL ) // so projectiles pass through
-
- SetShieldWallCPointOrigin( shieldWallFX, < BURN_CARD_WEAPON_HUD_COLOR[0], BURN_CARD_WEAPON_HUD_COLOR[1], BURN_CARD_WEAPON_HUD_COLOR[2] > )
-
- float tickRate = 0.1
- float dps = vortexSphere.GetMaxHealth() / duration
- float dmgAmount = dps * tickRate
-
- EmitSoundOnEntity( vortexSphere, "ShieldWall_Loop" )
-
- float endSoundTime = endTime - 3.0 // so magic
- thread PlayDelayedVortexEndSound( endSoundTime, vortexSphere )
- bool playedEndSound = false
- vector vortexOrigin = vortexSphere.GetOrigin()
- entity mover = CreateScriptMover()
-
- int weaponOwnerTeam = weaponOwner.GetTeam();
-
- OnThreadEnd(
- function() : ( vortexSphere, vortexOrigin, endTime, mover, weaponOwnerTeam )
- {
- if ( IsValid( vortexSphere ) )
- {
- StopSoundOnEntity( vortexSphere, "ShieldWall_Loop" )
- StopSoundOnEntity( vortexSphere, "ShieldWall_End" )
- }
-
- if ( IsValid( mover ) )
- mover.Destroy()
-
- if ( endTime - Time() >= 1.0 )
- EmitSoundAtPosition( weaponOwnerTeam, vortexOrigin, "ShieldWall_Destroyed" )
- }
- )
-
- int rampOuts = 3
- float rampOutTime = 0.75
- float rampOutFinalFade = 1.0
- float finalFadeExtraBuffer = 0.45
-
- wait duration - ( rampOutTime * rampOuts + rampOutFinalFade + finalFadeExtraBuffer )
- EmitSoundOnEntity( vortexSphere, "ShieldWall_End" )
-
- entity cpoint = GetShieldWallFXCPoint( shieldWallFX )
-
- vector cpointOrigin = cpoint.GetOrigin()
- mover.SetOrigin( cpointOrigin )
- cpoint.SetParent( mover )
- float rampTime1 = rampOutTime * 0.75
- float rampTime2 = rampOutTime - rampTime1
- for ( int i = 0; i < rampOuts; i++ )
- {
- mover.NonPhysicsMoveTo( <100,0,0>, rampTime1, rampTime1, 0.0 )
- wait rampTime1
- mover.NonPhysicsMoveTo( cpointOrigin, rampTime2, 0.0, rampTime2 )
- wait rampTime2
- }
-
- mover.NonPhysicsMoveTo( <0,0,0>, rampOutFinalFade, 0.0, 0.0 )
- wait rampOutFinalFade + finalFadeExtraBuffer
-}
-
-void function PlayDelayedVortexEndSound( float delay, entity vortexSphere )
-{
- vortexSphere.EndSignal( "OnDestroy" )
- wait delay
- EmitSoundOnEntity( vortexSphere, "ShieldWall_End" )
-}
-
-
-function DrainHealthOverTime( entity vortexSphere, entity shieldWallFX, float duration )
-{
- vortexSphere.EndSignal( "OnDestroy" )
- shieldWallFX.EndSignal( "OnDestroy" )
-
- float startTime = Time()
- float endTime = startTime + duration
-
- float tickRate = 0.1
- float dps = vortexSphere.GetMaxHealth() / duration
- float dmgAmount = dps * tickRate
-
- EmitSoundOnEntity( vortexSphere, "ShieldWall_Loop" )
-
- float endSoundTime = endTime - 3.0
- bool playedEndSound = false
- vector vortexOrigin = vortexSphere.GetOrigin()
-
- OnThreadEnd(
- function() : ( vortexSphere, vortexOrigin, endTime )
- {
- if ( endTime - Time() < 1.0 )
- return
-
- int teamNum = TEAM_UNASSIGNED
-
- if ( IsValid( vortexSphere ) )
- {
- StopSoundOnEntity( vortexSphere, "ShieldWall_Loop" )
- StopSoundOnEntity( vortexSphere, "ShieldWall_End" )
-
- teamNum = vortexSphere.GetTeam()
- }
-
- EmitSoundAtPosition( teamNum, vortexOrigin, "ShieldWall_Destroyed" )
- }
- )
-
- while ( Time() < endTime )
- {
- if ( Time() > endSoundTime && !playedEndSound )
- {
- EmitSoundOnEntity( vortexSphere, "ShieldWall_End" )
- playedEndSound = true
- }
-
- //vortexSphere.SetHealth( vortexSphere.GetHealth() - dmgAmount )
- UpdateShieldWallColorForFrac( shieldWallFX, GetHealthFrac( vortexSphere ) )
- wait tickRate
- }
-
- StopSoundOnEntity( vortexSphere, "ShieldWall_Loop" )
-}
-
-function UpdateShieldWallColorForFrac( entity shieldWallFX, float colorFrac )
-{
- vector color = GetShieldTriLerpColor( 1 - colorFrac )
-
- if ( IsValid( shieldWallFX ) )
- SetShieldWallCPointOrigin( shieldWallFX, color )
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// All functions that care about to-be-deprecated cpoint are below here:
-//
-////////////////////////////////////////////////////////////////////////////////////////////////
-
-void function PlayEffectOnVortexSphere( int fx, vector origin, vector angles, entity vortexSphere )
-{
- if ( !IsValid( vortexSphere ) )
- return
- if ( !IsValid( vortexSphere.e.shieldWallFX ) )
- return
- entity cpoint = vortexSphere.e.shieldWallFX.e.cpoint
- if ( !IsValid( cpoint ) )
- return
- StartParticleEffectInWorldWithControlPoint( fx, origin, angles, cpoint.GetOrigin() )
-}
-
-void function SetVortexSphereShieldWallCPoint( entity vortexSphere, entity cpoint )
-{
- Assert( IsValid( vortexSphere ) )
- Assert( IsValid( vortexSphere.e.shieldWallFX ) )
- SetShieldWallCPoint( vortexSphere.e.shieldWallFX, cpoint )
-}
-
-void function SetShieldWallCPoint( entity shieldWallFX, entity cpoint )
-{
- Assert( IsValid( shieldWallFX ) )
- Assert( IsValid( cpoint ) )
- shieldWallFX.e.cpoint = cpoint
-}
-
-void function StopShieldWallFX( entity vortexSphere )
-{
- entity shieldWallFX = vortexSphere.e.shieldWallFX
- vortexSphere.e.shieldWallFX = null
-
- if ( !IsValid( shieldWallFX ) )
- return
-
- shieldWallFX.Fire( "StopPlayEndCap" )
- shieldWallFX.Fire( "Kill", "", 1.0 )
-
- if ( IsValid( shieldWallFX.e.cpoint ) )
- shieldWallFX.e.cpoint.Fire( "Kill", "", 1.0 )
- EffectStop( shieldWallFX )
-}
-
-void function StopShieldWallFXOverTime( entity vortexSphere, float duration )
-{
- entity shieldWallFX = vortexSphere.e.shieldWallFX
- shieldWallFX.Fire( "StopPlayEndCap", "", duration )
- shieldWallFX.Fire( "Kill", "", duration )
- shieldWallFX.e.cpoint.Fire( "Kill", "", duration )
-}
-
-void function SetShieldWallCPointOrigin( entity shieldWallFX, vector AT_TURRET_SHIELD_COLOR )
-{
- Assert( IsValid( shieldWallFX ) )
- if ( !IsValid( shieldWallFX.e.cpoint ) )
- return
- shieldWallFX.e.cpoint.SetOrigin( AT_TURRET_SHIELD_COLOR )
-}
-
-entity function GetShieldWallFXCPoint( entity shieldWallFX )
-{
- Assert( IsValid( shieldWallFX.e.cpoint ) )
- return shieldWallFX.e.cpoint
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_team_emp.gnut b/Northstar.CustomServers/scripts/vscripts/weapons/_team_emp.gnut
deleted file mode 100644
index 41d42848..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_team_emp.gnut
+++ /dev/null
@@ -1,38 +0,0 @@
-global function TeamEMP_Init
-global function EMPEffects
-
-void function TeamEMP_Init()
-{
- RegisterSignal( "PlayerEMPed" )
-}
-
-void function EMPEffects( entity player, float time )
-{
- player.nv.empEndTime = Time() + time
-
- player.Signal( "PlayerEMPed" )
-
- // remember this is a stack so you need to enable as many times as you disable
- DisableOffhandWeapons( player )
-
- thread RecoverFromEMP( player, time )
-}
-
-void function RecoverFromEMP( entity player, float time )
-{
- svGlobal.levelEnt.EndSignal( "BurnMeter_PreMatchEnter" )
- player.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( player )
- {
- if ( IsValid( player ) )
- {
- // remember this is a stack so you need to enable as many times as you disable
- EnableOffhandWeapons( player )
- }
- }
- )
-
- wait time + 0.1
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_vortex.nut b/Northstar.CustomServers/scripts/vscripts/weapons/_vortex.nut
deleted file mode 100644
index f1e46a53..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_vortex.nut
+++ /dev/null
@@ -1,1983 +0,0 @@
-untyped
-
-global function Vortex_Init
-
-global function CreateVortexSphere
-global function DestroyVortexSphereFromVortexWeapon
-global function EnableVortexSphere
-#if SERVER
-global function ValidateVortexImpact
-global function TryVortexAbsorb
-global function SetVortexSphereBulletHitRules
-global function SetVortexSphereProjectileHitRules
-#endif
-global function VortexDrainedByImpact
-global function VortexPrimaryAttack
-global function GetVortexSphereCurrentColor
-global function GetShieldTriLerpColor
-global function IsVortexing
-#if SERVER
-global function Vortex_HandleElectricDamage
-global function VortexSphereDrainHealthForDamage
-global function Vortex_CreateImpactEventData
-global function Vortex_SpawnHeatShieldPingFX
-#endif
-
-global function Vortex_SetTagName
-global function Vortex_SetBulletCollectionOffset
-
-global function CodeCallback_OnVortexHitBullet
-global function CodeCallback_OnVortexHitProjectile
-
-const AMPED_WALL_IMPACT_FX = $"P_impact_xo_shield_cp"
-
-global const PROTO_AMPED_WALL = "proto_amped_wall"
-global const GUN_SHIELD_WALL = "gun_shield_wall"
-const PROX_MINE_MODEL = $"models/weapons/caber_shot/caber_shot_thrown.mdl"
-
-const VORTEX_SPHERE_COLOR_CHARGE_FULL = <115, 247, 255> // blue
-const VORTEX_SPHERE_COLOR_CHARGE_MED = <200, 128, 80> // orange
-const VORTEX_SPHERE_COLOR_CHARGE_EMPTY = <200, 80, 80> // red
-const VORTEX_SPHERE_COLOR_PAS_ION_VORTEX = <115, 174, 255> // blue
-const AMPED_DAMAGE_SCALAR = 1.5
-
-const VORTEX_SPHERE_COLOR_CROSSOVERFRAC_FULL2MED = 0.75 // from zero to this fraction, fade between full and medium charge colors
-const VORTEX_SPHERE_COLOR_CROSSOVERFRAC_MED2EMPTY = 0.95 // from "full2med" to this fraction, fade between medium and empty charge colors
-
-const VORTEX_BULLET_ABSORB_COUNT_MAX = 32
-const VORTEX_PROJECTILE_ABSORB_COUNT_MAX = 32
-
-const VORTEX_TIMED_EXPLOSIVE_FUSETIME = 2.75 // fuse time for absorbed projectiles
-const VORTEX_TIMED_EXPLOSIVE_FUSETIME_WARNINGFRAC = 0.75 // wait this fraction of the fuse time before warning the player it's about to explode
-
-const VORTEX_EXP_ROUNDS_RETURN_SPREAD_XY = 0.15
-const VORTEX_EXP_ROUNDS_RETURN_SPREAD_Z = 0.075
-
-const VORTEX_ELECTRIC_DAMAGE_CHARGE_DRAIN_MIN = 0.1 // fraction of charge time
-const VORTEX_ELECTRIC_DAMAGE_CHARGE_DRAIN_MAX = 0.3
-
-//The shotgun spams a lot of pellets that deal too much damage if they return full damage.
-const VORTEX_SHOTGUN_DAMAGE_RATIO = 0.25
-
-
-const SHIELD_WALL_BULLET_FX = $"P_impact_xo_shield_cp"
-const SHIELD_WALL_EXPMED_FX = $"P_impact_exp_med_xo_shield_CP"
-
-const SIGNAL_ID_BULLET_HIT_THINK = "signal_id_bullet_hit_think"
-
-const VORTEX_EXPLOSIVE_WARNING_SFX_LOOP = "Weapon_Vortex_Gun.ExplosiveWarningBeep"
-
-const VORTEX_PILOT_WEAPON_WEAKNESS_DAMAGESCALE = 6.0
-
-// These match the strings in the WeaponEd dropdown box for vortex_refire_behavior
-global const VORTEX_REFIRE_NONE = ""
-global const VORTEX_REFIRE_ABSORB = "absorb"
-global const VORTEX_REFIRE_BULLET = "bullet"
-global const VORTEX_REFIRE_EXPLOSIVE_ROUND = "explosive_round"
-global const VORTEX_REFIRE_ROCKET = "rocket"
-global const VORTEX_REFIRE_GRENADE = "grenade"
-global const VORTEX_REFIRE_GRENADE_LONG_FUSE = "grenade_long_fuse"
-
-const VortexIgnoreClassnames = {
- ["mp_titancore_flame_wave"] = true,
- ["mp_ability_grapple"] = true,
- ["mp_ability_shifter"] = true,
-}
-
-table vortexImpactWeaponInfo
-
-const DEG_COS_60 = cos( 60 * DEG_TO_RAD )
-
-function Vortex_Init()
-{
- PrecacheParticleSystem( SHIELD_WALL_BULLET_FX )
- GetParticleSystemIndex( SHIELD_WALL_BULLET_FX )
- PrecacheParticleSystem( SHIELD_WALL_EXPMED_FX )
- GetParticleSystemIndex( SHIELD_WALL_EXPMED_FX )
- PrecacheParticleSystem( AMPED_WALL_IMPACT_FX )
- GetParticleSystemIndex( AMPED_WALL_IMPACT_FX )
-
- RegisterSignal( SIGNAL_ID_BULLET_HIT_THINK )
- RegisterSignal( "VortexStopping" )
-
- RegisterSignal( "VortexAbsorbed" )
- RegisterSignal( "VortexFired" )
- RegisterSignal( "Script_OnDamaged" )
-}
-
-#if SERVER
-var function VortexBulletHitRules_Default( entity vortexSphere, var damageInfo )
-{
- return damageInfo
-}
-
-bool function VortexProjectileHitRules_Default( entity vortexSphere, entity attacker, bool takesDamageByDefault )
-{
- return takesDamageByDefault
-}
-
-void function SetVortexSphereBulletHitRules( entity vortexSphere, var functionref( entity, var ) customRules )
-{
- vortexSphere.e.BulletHitRules = customRules
-}
-
-void function SetVortexSphereProjectileHitRules( entity vortexSphere, bool functionref( entity, entity, bool ) customRules )
-{
- vortexSphere.e.ProjectileHitRules = customRules
-}
-#endif
-function CreateVortexSphere( entity vortexWeapon, bool useCylinderCheck, bool blockOwnerWeapon, int sphereRadius = 40, int bulletFOV = 180 )
-{
- entity owner = vortexWeapon.GetWeaponOwner()
- Assert( owner )
-
- #if SERVER
- //printt( "util ent:", vortexWeapon.GetWeaponUtilityEntity() )
- Assert ( !vortexWeapon.GetWeaponUtilityEntity(), "Tried to create more than one vortex sphere on a vortex weapon!" )
-
- entity vortexSphere = CreateEntity( "vortex_sphere" )
- Assert( vortexSphere )
-
- int spawnFlags = SF_ABSORB_BULLETS | SF_BLOCK_NPC_WEAPON_LOF
-
- if ( useCylinderCheck )
- {
- spawnFlags = spawnFlags | SF_ABSORB_CYLINDER
- vortexSphere.kv.height = sphereRadius * 2
- }
-
- if ( blockOwnerWeapon )
- spawnFlags = spawnFlags | SF_BLOCK_OWNER_WEAPON
-
- vortexSphere.kv.spawnflags = spawnFlags
-
- vortexSphere.kv.enabled = 0
- vortexSphere.kv.radius = sphereRadius
- vortexSphere.kv.bullet_fov = bulletFOV
- vortexSphere.kv.physics_pull_strength = 25
- vortexSphere.kv.physics_side_dampening = 6
- vortexSphere.kv.physics_fov = 360
- vortexSphere.kv.physics_max_mass = 2
- vortexSphere.kv.physics_max_size = 6
- Assert( owner.IsNPC() || owner.IsPlayer(), "Vortex script expects the weapon owner to be a player or NPC." )
-
- SetVortexSphereBulletHitRules( vortexSphere, VortexBulletHitRules_Default )
- SetVortexSphereProjectileHitRules( vortexSphere, VortexProjectileHitRules_Default )
-
- DispatchSpawn( vortexSphere )
-
- vortexSphere.SetOwner( owner )
-
- if ( owner.IsNPC() )
- {
- vortexSphere.SetParent( owner, "PROPGUN" )
- vortexSphere.SetLocalOrigin( Vector( 0, 35, 0 ) )
- }
- else
- {
- vortexSphere.SetParent( owner )
- vortexSphere.SetLocalOrigin( Vector( 0, 10, -30 ) )
- }
- vortexSphere.SetAbsAngles( Vector( 0, 0, 0 ) ) //Setting local angles on a parented object is not supported
-
- vortexSphere.SetOwnerWeapon( vortexWeapon )
- vortexWeapon.SetWeaponUtilityEntity( vortexSphere )
- #endif
-
- SetVortexAmmo( vortexWeapon, 0 )
-}
-
-
-function EnableVortexSphere( entity vortexWeapon )
-{
- string tagname = GetVortexTagName( vortexWeapon )
- entity weaponOwner = vortexWeapon.GetWeaponOwner()
- local hasBurnMod = vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod )
-
- #if SERVER
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
- Assert( vortexSphere )
- vortexSphere.FireNow( "Enable" )
-
- thread SetPlayerUsingVortex( weaponOwner, vortexWeapon )
-
- Vortex_CreateAbsorbFX_ControlPoints( vortexWeapon )
-
- // world (3P) version of the vortex sphere FX
- vortexSphere.s.worldFX <- CreateEntity( "info_particle_system" )
-
- if ( hasBurnMod )
- {
- if ( "fxChargingControlPointBurn" in vortexWeapon.s )
- vortexSphere.s.worldFX.SetValueForEffectNameKey( expect asset( vortexWeapon.s.fxChargingControlPointBurn ) )
- }
- else
- {
- if ( "fxChargingControlPoint" in vortexWeapon.s )
- vortexSphere.s.worldFX.SetValueForEffectNameKey( expect asset( vortexWeapon.s.fxChargingControlPoint ) )
- }
-
- vortexSphere.s.worldFX.kv.start_active = 1
- vortexSphere.s.worldFX.SetOwner( weaponOwner )
- vortexSphere.s.worldFX.SetParent( vortexWeapon, tagname )
- vortexSphere.s.worldFX.kv.VisibilityFlags = (ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY) // not owner only
- vortexSphere.s.worldFX.kv.cpoint1 = vortexWeapon.s.vortexSphereColorCP.GetTargetName()
- vortexSphere.s.worldFX.SetStopType( "destroyImmediately" )
-
- DispatchSpawn( vortexSphere.s.worldFX )
- #endif
-
- SetVortexAmmo( vortexWeapon, 0 )
-
- #if CLIENT
- if ( IsLocalViewPlayer( weaponOwner ) )
- {
- local fxAlias = null
-
- if ( hasBurnMod )
- {
- if ( "fxChargingFPControlPointBurn" in vortexWeapon.s )
- fxAlias = vortexWeapon.s.fxChargingFPControlPointBurn
- }
- else
- {
- if ( "fxChargingFPControlPoint" in vortexWeapon.s )
- fxAlias = vortexWeapon.s.fxChargingFPControlPoint
- }
-
- if ( fxAlias )
- {
- int sphereClientFXHandle = vortexWeapon.PlayWeaponEffectReturnViewEffectHandle( fxAlias, $"", tagname )
- thread VortexSphereColorUpdate( vortexWeapon, sphereClientFXHandle )
- }
- }
- #elseif SERVER
- asset fxAlias = $""
-
- if ( hasBurnMod )
- {
- if ( "fxChargingFPControlPointReplayBurn" in vortexWeapon.s )
- fxAlias = expect asset( vortexWeapon.s.fxChargingFPControlPointReplayBurn )
- }
- else
- {
- if ( "fxChargingFPControlPointReplay" in vortexWeapon.s )
- fxAlias = expect asset( vortexWeapon.s.fxChargingFPControlPointReplay )
- }
-
- if ( fxAlias != $"" )
- vortexWeapon.PlayWeaponEffect( fxAlias, $"", tagname )
-
- thread VortexSphereColorUpdate( vortexWeapon )
- #endif
-}
-
-
-function DestroyVortexSphereFromVortexWeapon( entity vortexWeapon )
-{
- DisableVortexSphereFromVortexWeapon( vortexWeapon )
-
- #if SERVER
- DestroyVortexSphere( vortexWeapon.GetWeaponUtilityEntity() )
- vortexWeapon.SetWeaponUtilityEntity( null )
- #endif
-}
-
-void function DestroyVortexSphere( entity vortexSphere )
-{
- if ( IsValid( vortexSphere ) )
- {
- vortexSphere.s.worldFX.Destroy()
- vortexSphere.Destroy()
- }
-}
-
-
-function DisableVortexSphereFromVortexWeapon( entity vortexWeapon )
-{
- vortexWeapon.Signal( "VortexStopping" )
-
- // server cleanup
- #if SERVER
- DisableVortexSphere( vortexWeapon.GetWeaponUtilityEntity() )
- Vortex_CleanupAllEffects( vortexWeapon )
- Vortex_ClearImpactEventData( vortexWeapon )
- #endif
-
- // client & server cleanup
-
- if ( vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- {
- if ( "fxChargingFPControlPointBurn" in vortexWeapon.s )
- vortexWeapon.StopWeaponEffect( expect asset( vortexWeapon.s.fxChargingFPControlPointBurn ), $"" )
- if ( "fxChargingFPControlPointReplayBurn" in vortexWeapon.s )
- vortexWeapon.StopWeaponEffect( expect asset( vortexWeapon.s.fxChargingFPControlPointReplayBurn ), $"" )
- }
- else
- {
- if ( "fxChargingFPControlPoint" in vortexWeapon.s )
- vortexWeapon.StopWeaponEffect( expect asset( vortexWeapon.s.fxChargingFPControlPoint ), $"" )
- if ( "fxChargingFPControlPointReplay" in vortexWeapon.s )
- vortexWeapon.StopWeaponEffect( expect asset( vortexWeapon.s.fxChargingFPControlPointReplay ), $"" )
- }
-}
-
-void function DisableVortexSphere( entity vortexSphere )
-{
- if ( IsValid( vortexSphere ) )
- {
- vortexSphere.FireNow( "Disable" )
- vortexSphere.Signal( SIGNAL_ID_BULLET_HIT_THINK )
- }
-
-}
-
-
-#if SERVER
-function Vortex_CreateAbsorbFX_ControlPoints( entity vortexWeapon )
-{
- entity player = vortexWeapon.GetWeaponOwner()
- Assert( player )
-
- // vortex swirling incoming rounds FX location control point
- if ( !( "vortexBulletEffectCP" in vortexWeapon.s ) )
- vortexWeapon.s.vortexBulletEffectCP <- null
- vortexWeapon.s.vortexBulletEffectCP = CreateEntity( "info_placement_helper" )
- SetTargetName( expect entity( vortexWeapon.s.vortexBulletEffectCP ), UniqueString( "vortexBulletEffectCP" ) )
- vortexWeapon.s.vortexBulletEffectCP.kv.start_active = 1
-
- DispatchSpawn( vortexWeapon.s.vortexBulletEffectCP )
-
- vector offset = GetBulletCollectionOffset( vortexWeapon )
- vector origin = player.OffsetPositionFromView( player.EyePosition(), offset )
-
- vortexWeapon.s.vortexBulletEffectCP.SetOrigin( origin )
- vortexWeapon.s.vortexBulletEffectCP.SetParent( player )
-
- // vortex sphere color control point
- if ( !( "vortexSphereColorCP" in vortexWeapon.s ) )
- vortexWeapon.s.vortexSphereColorCP <- null
- vortexWeapon.s.vortexSphereColorCP = CreateEntity( "info_placement_helper" )
- SetTargetName( expect entity( vortexWeapon.s.vortexSphereColorCP ), UniqueString( "vortexSphereColorCP" ) )
- vortexWeapon.s.vortexSphereColorCP.kv.start_active = 1
-
- DispatchSpawn( vortexWeapon.s.vortexSphereColorCP )
-}
-
-
-function Vortex_CleanupAllEffects( entity vortexWeapon )
-{
- Assert( IsServer() )
-
- Vortex_CleanupImpactAbsorbFX( vortexWeapon )
-
- if ( ( "vortexBulletEffectCP" in vortexWeapon.s ) && IsValid_ThisFrame( expect entity( vortexWeapon.s.vortexBulletEffectCP ) ) )
- vortexWeapon.s.vortexBulletEffectCP.Destroy()
-
- if ( ( "vortexSphereColorCP" in vortexWeapon.s ) && IsValid_ThisFrame( expect entity( vortexWeapon.s.vortexSphereColorCP ) ) )
- vortexWeapon.s.vortexSphereColorCP.Destroy()
-}
-#endif // SERVER
-
-
-function SetPlayerUsingVortex( entity weaponOwner, entity vortexWeapon )
-{
- weaponOwner.EndSignal( "OnDeath" )
-
- weaponOwner.s.isVortexing <- true
-
- vortexWeapon.WaitSignal( "VortexStopping" )
-
- OnThreadEnd
- (
- function() : ( weaponOwner )
- {
- if ( IsValid_ThisFrame( weaponOwner ) && "isVortexing" in weaponOwner.s )
- {
- delete weaponOwner.s.isVortexing
- }
- }
- )
-}
-
-
-function IsVortexing( entity ent )
-{
- Assert( IsServer() )
-
- if ( "isVortexing" in ent.s )
- return true
-}
-
-
-#if SERVER
-function Vortex_HandleElectricDamage( entity ent, entity attacker, damage, entity weapon )
-{
- if ( !IsValid( ent ) )
- return damage
-
- if ( !ent.IsTitan() )
- return damage
-
- if ( !ent.IsPlayer() && !ent.IsNPC() )
- return damage
-
- if ( !IsVortexing( ent ) )
- return damage
-
- entity vortexWeapon = ent.GetActiveWeapon()
- if ( !IsValid( vortexWeapon ) )
- return damage
-
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
- if ( !IsValid( vortexSphere ) )
- return damage
-
- if ( !IsValid( vortexWeapon ) || !IsValid( vortexSphere ) )
- return damage
-
- // vortex FOV check
- //printt( "sphere FOV:", vortexSphere.kv.bullet_fov )
- local sphereFOV = vortexSphere.kv.bullet_fov.tointeger()
- entity attackerWeapon = attacker.GetActiveWeapon()
- int attachIdx = attackerWeapon.LookupAttachment( "muzzle_flash" )
- vector beamOrg = attackerWeapon.GetAttachmentOrigin( attachIdx )
- vector firingDir = beamOrg - vortexSphere.GetOrigin()
- firingDir = Normalize( firingDir )
- vector vortexDir = AnglesToForward( vortexSphere.GetAngles() )
-
- float dot = DotProduct( vortexDir, firingDir )
-
- float degCos = DEG_COS_60
- if ( sphereFOV != 120 )
- deg_cos( sphereFOV * 0.5 )
-
- // not in the vortex cone
- if ( dot < degCos )
- return damage
-
- if ( "fxElectricalExplosion" in vortexWeapon.s )
- {
- entity fxRef = CreateEntity( "info_particle_system" )
- fxRef.SetValueForEffectNameKey( expect asset( vortexWeapon.s.fxElectricalExplosion ) )
- fxRef.kv.start_active = 1
- fxRef.SetStopType( "destroyImmediately" )
- //fxRef.kv.VisibilityFlags = ENTITY_VISIBLE_TO_OWNER // HACK this turns on owner only visibility. Uncomment when we hook up dedicated 3P effects
- fxRef.SetOwner( ent )
- fxRef.SetOrigin( vortexSphere.GetOrigin() )
- fxRef.SetParent( ent )
-
- DispatchSpawn( fxRef )
- fxRef.Kill_Deprecated_UseDestroyInstead( 1 )
- }
-
- return 0
-}
-
-// this function handles all incoming vortex impact events
-bool function TryVortexAbsorb( entity vortexSphere, entity attacker, vector origin, int damageSourceID, entity weapon, string weaponName, string impactType, entity projectile = null, damageType = null, reflect = false )
-{
- if ( weaponName in VortexIgnoreClassnames )
- return false
-
- entity vortexWeapon = vortexSphere.GetOwnerWeapon()
- entity owner = vortexWeapon.GetWeaponOwner()
-
- // keep cycling the oldest hitscan bullets out
- if( !reflect )
- {
- if ( impactType == "hitscan" )
- Vortex_ClampAbsorbedBulletCount( vortexWeapon )
- else
- Vortex_ClampAbsorbedProjectileCount( vortexWeapon )
- }
-
- // vortex spheres tag refired projectiles with info about the original projectile for accurate duplication when re-absorbed
- if ( projectile )
- {
-
- // specifically for tether, since it gets moved to the vortex area and can get absorbed in the process, then destroyed
- if ( !IsValid( projectile ) )
- return false
-
- entity projOwner = projectile.GetOwner()
- if ( IsValid( projOwner ) && projOwner.GetTeam() == owner.GetTeam() )
- return false
-
- if ( projectile.proj.hasBouncedOffVortex )
- return false
-
- if ( projectile.ProjectileGetWeaponInfoFileKeyField( "projectile_ignores_vortex" ) == "fall_vortex" )
- {
- vector velocity = projectile.GetVelocity()
- vector multiplier = < -0.25, -0.25, -0.25 >
- velocity = < velocity.x * multiplier.x, velocity.y * multiplier.y, velocity.z * multiplier.z >
- projectile.SetVelocity( velocity )
- projectile.proj.hasBouncedOffVortex = true
- return false
- }
-
- // if ( projectile.GetParent() == owner )
- // return false
-
- if ( "originalDamageSource" in projectile.s )
- {
- damageSourceID = expect int( projectile.s.originalDamageSource )
-
- // Vortex Volley Achievement
- if ( IsValid( owner ) && owner.IsPlayer() )
- {
- //if ( PlayerProgressionAllowed( owner ) )
- // SetAchievement( owner, "ach_vortexVolley", true )
- }
- }
-
- // Max projectile stat tracking
- int projectilesInVortex = 1
- projectilesInVortex += vortexWeapon.w.vortexImpactData.len()
-
- if ( IsValid( owner ) && owner.IsPlayer() )
- {
- if ( PlayerProgressionAllowed( owner ) )
- {
- int record = owner.GetPersistentVarAsInt( "mostProjectilesCollectedInVortex" )
- if ( projectilesInVortex > record )
- owner.SetPersistentVar( "mostProjectilesCollectedInVortex", projectilesInVortex )
- }
-
- var impact_sound_1p = projectile.ProjectileGetWeaponInfoFileKeyField( "vortex_impact_sound_1p" )
- if ( impact_sound_1p != null )
- EmitSoundOnEntityOnlyToPlayer( vortexSphere, owner, impact_sound_1p )
- }
-
- var impact_sound_3p = projectile.ProjectileGetWeaponInfoFileKeyField( "vortex_impact_sound_3p" )
- if ( impact_sound_3p != null )
- EmitSoundAtPosition( TEAM_UNASSIGNED, origin, impact_sound_3p )
- }
- else
- {
- if ( IsValid( owner ) && owner.IsPlayer() )
- {
- var impact_sound_1p = GetWeaponInfoFileKeyField_Global( weaponName, "vortex_impact_sound_1p" )
- if ( impact_sound_1p != null )
- EmitSoundOnEntityOnlyToPlayer( vortexSphere, owner, impact_sound_1p )
- }
-
- var impact_sound_3p = GetWeaponInfoFileKeyField_Global( weaponName, "vortex_impact_sound_3p" )
- if ( impact_sound_3p != null )
- EmitSoundAtPosition( TEAM_UNASSIGNED, origin, impact_sound_3p )
- }
-
- local impactData = Vortex_CreateImpactEventData( vortexWeapon, attacker, origin, damageSourceID, weaponName, impactType )
-
- VortexDrainedByImpact( vortexWeapon, weapon, projectile, damageType )
- Vortex_NotifyAttackerDidDamage( expect entity( impactData.attacker ), owner, impactData.origin )
-
- if ( impactData.refireBehavior == VORTEX_REFIRE_ABSORB )
- return true
-
- if ( vortexWeapon.GetWeaponClassName() == "mp_titanweapon_heat_shield" )
- return true
-
- if ( !Vortex_ScriptCanHandleImpactEvent( impactData ) )
- return false
-
- Vortex_StoreImpactEvent( vortexWeapon, impactData )
-
- VortexImpact_PlayAbsorbedFX( vortexWeapon, impactData )
-
- if ( impactType == "hitscan" )
- vortexSphere.AddBulletToSphere();
- else
- vortexSphere.AddProjectileToSphere();
-
- local maxShotgunPelletsToIgnore = VORTEX_BULLET_ABSORB_COUNT_MAX * ( 1 - VORTEX_SHOTGUN_DAMAGE_RATIO )
- if ( IsPilotShotgunWeapon( weaponName ) && ( vortexWeapon.s.shotgunPelletsToIgnore + 1 ) < maxShotgunPelletsToIgnore )
- vortexWeapon.s.shotgunPelletsToIgnore += ( 1 - VORTEX_SHOTGUN_DAMAGE_RATIO )
-
- if ( reflect )
- {
- local attackParams = {}
- attackParams.pos <- owner.EyePosition()
- attackParams.dir <- owner.GetPlayerOrNPCViewVector()
-
- int bulletsFired = VortexReflectAttack( vortexWeapon, attackParams, expect vector( impactData.origin ) )
-
- Vortex_CleanupImpactAbsorbFX( vortexWeapon )
- Vortex_ClearImpactEventData( vortexWeapon )
-
- while ( vortexSphere.GetBulletAbsorbedCount() > 0 )
- vortexSphere.RemoveBulletFromSphere();
-
- while ( vortexSphere.GetProjectileAbsorbedCount() > 0 )
- vortexSphere.RemoveProjectileFromSphere();
- }
-
- return true
-}
-#endif // SERVER
-
-function VortexDrainedByImpact( entity vortexWeapon, entity weapon, entity projectile, damageType )
-{
- if ( vortexWeapon.HasMod( "unlimited_charge_time" ) )
- return
- if ( vortexWeapon.HasMod( "vortex_extended_effect_and_no_use_penalty" ) )
- return
-
- float amount
- if ( projectile )
- amount = projectile.GetProjectileWeaponSettingFloat( eWeaponVar.vortex_drain )
- else
- amount = weapon.GetWeaponSettingFloat( eWeaponVar.vortex_drain )
-
- if ( amount <= 0.0 )
- return
-
- if ( vortexWeapon.GetWeaponClassName() == "mp_titanweapon_vortex_shield_ion" )
- {
- entity owner = vortexWeapon.GetWeaponOwner()
- int totalEnergy = owner.GetSharedEnergyTotal()
- owner.TakeSharedEnergy( int( float( totalEnergy ) * amount ) )
- }
- else
- {
- float frac = min ( vortexWeapon.GetWeaponChargeFraction() + amount, 1.0 )
- vortexWeapon.SetWeaponChargeFraction( frac )
- }
-}
-
-
-function VortexSlowOwnerFromAttacker( entity player, entity attacker, vector velocity, float multiplier )
-{
- vector damageForward = player.GetOrigin() - attacker.GetOrigin()
- damageForward.z = 0
- damageForward.Norm()
-
- vector velForward = player.GetVelocity()
- velForward.z = 0
- velForward.Norm()
-
- float dot = DotProduct( velForward, damageForward )
- if ( dot >= -0.5 )
- return
-
- dot += 0.5
- dot *= -2.0
-
- vector negateVelocity = velocity * -multiplier
- negateVelocity *= dot
-
- velocity += negateVelocity
- player.SetVelocity( velocity )
-}
-
-
-#if SERVER
-function Vortex_ClampAbsorbedBulletCount( entity vortexWeapon )
-{
- if ( GetBulletsAbsorbedCount( vortexWeapon ) >= ( VORTEX_BULLET_ABSORB_COUNT_MAX - 1 ) )
- Vortex_RemoveOldestAbsorbedBullet( vortexWeapon )
-}
-
-function Vortex_ClampAbsorbedProjectileCount( entity vortexWeapon )
-{
- if ( GetProjectilesAbsorbedCount( vortexWeapon ) >= ( VORTEX_PROJECTILE_ABSORB_COUNT_MAX - 1 ) )
- Vortex_RemoveOldestAbsorbedProjectile( vortexWeapon )
-}
-
-function Vortex_RemoveOldestAbsorbedBullet( entity vortexWeapon )
-{
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
-
- local bulletImpacts = Vortex_GetHitscanBulletImpacts( vortexWeapon )
- local impactDataToRemove = bulletImpacts[ 0 ] // since it's an array, the first one will be the oldest
-
- Vortex_RemoveImpactEvent( vortexWeapon, impactDataToRemove )
-
- vortexSphere.RemoveBulletFromSphere()
-}
-
-function Vortex_RemoveOldestAbsorbedProjectile( entity vortexWeapon )
-{
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
-
- local projImpacts = Vortex_GetProjectileImpacts( vortexWeapon )
- local impactDataToRemove = projImpacts[ 0 ] // since it's an array, the first one will be the oldest
-
- Vortex_RemoveImpactEvent( vortexWeapon, impactDataToRemove )
-
- vortexSphere.RemoveProjectileFromSphere()
-}
-
-function Vortex_CreateImpactEventData( entity vortexWeapon, entity attacker, vector origin, int damageSourceID, string weaponName, string impactType )
-{
- entity player = vortexWeapon.GetWeaponOwner()
- local impactData = {}
-
- impactData.attacker <- attacker
- impactData.origin <- origin
- impactData.damageSourceID <- damageSourceID
- impactData.weaponName <- weaponName
- impactData.impactType <- impactType
-
- impactData.refireBehavior <- VORTEX_REFIRE_NONE
- impactData.absorbSFX <- "Vortex_Shield_AbsorbBulletSmall"
- impactData.absorbSFX_1p_vs_3p <- null
-
- impactData.team <- null
- // sets a team even if the attacker disconnected
- if ( IsValid_ThisFrame( attacker ) )
- {
- impactData.team = attacker.GetTeam()
- }
- else
- {
- // default to opposite team
- if ( player.GetTeam() == TEAM_IMC )
- impactData.team = TEAM_MILITIA
- else
- impactData.team = TEAM_IMC
- }
-
- impactData.absorbFX <- null
- impactData.absorbFX_3p <- null
- impactData.fxEnt_absorb <- null
-
- impactData.explosionradius <- null
- impactData.explosion_damage <- null
- impactData.impact_effect_table <- -1
- // -- everything from here down relies on being able to read a megaweapon file
- if ( !( impactData.weaponName in vortexImpactWeaponInfo ) )
- {
- vortexImpactWeaponInfo[ impactData.weaponName ] <- {}
- vortexImpactWeaponInfo[ impactData.weaponName ].absorbFX <- GetWeaponInfoFileKeyFieldAsset_Global( impactData.weaponName, "vortex_absorb_effect" )
- vortexImpactWeaponInfo[ impactData.weaponName ].absorbFX_3p <- GetWeaponInfoFileKeyFieldAsset_Global( impactData.weaponName, "vortex_absorb_effect_third_person" )
- vortexImpactWeaponInfo[ impactData.weaponName ].refireBehavior <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "vortex_refire_behavior" )
- vortexImpactWeaponInfo[ impactData.weaponName ].absorbSound <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "vortex_absorb_sound" )
- vortexImpactWeaponInfo[ impactData.weaponName ].absorbSound_1p_vs_3p <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "vortex_absorb_sound_1p_vs_3p" )
- vortexImpactWeaponInfo[ impactData.weaponName ].explosionradius <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "explosionradius" )
- vortexImpactWeaponInfo[ impactData.weaponName ].explosion_damage_heavy_armor <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "explosion_damage_heavy_armor" )
- vortexImpactWeaponInfo[ impactData.weaponName ].explosion_damage <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "explosion_damage" )
- vortexImpactWeaponInfo[ impactData.weaponName ].impact_effect_table <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "impact_effect_table" )
- vortexImpactWeaponInfo[ impactData.weaponName ].grenade_ignition_time <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "grenade_ignition_time" )
- vortexImpactWeaponInfo[ impactData.weaponName ].grenade_fuse_time <- GetWeaponInfoFileKeyField_Global( impactData.weaponName, "grenade_fuse_time" )
- }
-
- impactData.absorbFX = vortexImpactWeaponInfo[ impactData.weaponName ].absorbFX
- impactData.absorbFX_3p = vortexImpactWeaponInfo[ impactData.weaponName ].absorbFX_3p
- if ( impactData.absorbFX )
- Assert( impactData.absorbFX_3p, "Missing 3rd person absorb effect for " + impactData.weaponName )
- impactData.refireBehavior = vortexImpactWeaponInfo[ impactData.weaponName ].refireBehavior
-
- local absorbSound = vortexImpactWeaponInfo[ impactData.weaponName ].absorbSound
- if ( absorbSound )
- impactData.absorbSFX = absorbSound
-
- local absorbSound_1p_vs_3p = vortexImpactWeaponInfo[ impactData.weaponName ].absorbSound_1p_vs_3p
- if ( absorbSound_1p_vs_3p )
- impactData.absorbSFX_1p_vs_3p = absorbSound_1p_vs_3p
-
- // info we need for refiring (some types of) impacts
- impactData.explosionradius = vortexImpactWeaponInfo[ impactData.weaponName ].explosionradius
- impactData.explosion_damage = vortexImpactWeaponInfo[ impactData.weaponName ].explosion_damage_heavy_armor
- if ( impactData.explosion_damage == null )
- impactData.explosion_damage = vortexImpactWeaponInfo[ impactData.weaponName ].explosion_damage
- impactData.impact_effect_table = vortexImpactWeaponInfo[ impactData.weaponName ].impact_effect_table
-
- return impactData
-}
-
-function Vortex_ScriptCanHandleImpactEvent( impactData )
-{
- if ( impactData.refireBehavior == VORTEX_REFIRE_NONE )
- return false
-
- if ( !impactData.absorbFX )
- return false
-
- if ( impactData.impactType == "projectile" && !impactData.impact_effect_table )
- return false
-
- return true
-}
-
-function Vortex_StoreImpactEvent( entity vortexWeapon, impactData )
-{
- vortexWeapon.w.vortexImpactData.append( impactData )
-}
-
-// safely removes data for a single impact event
-function Vortex_RemoveImpactEvent( entity vortexWeapon, impactData )
-{
- Vortex_ImpactData_KillAbsorbFX( impactData )
-
- vortexWeapon.w.vortexImpactData.fastremovebyvalue( impactData )
-}
-
-function Vortex_GetAllImpactEvents( entity vortexWeapon )
-{
- return vortexWeapon.w.vortexImpactData
-}
-
-function Vortex_ClearImpactEventData( entity vortexWeapon )
-{
- vortexWeapon.w.vortexImpactData = []
-}
-
-function VortexImpact_PlayAbsorbedFX( entity vortexWeapon, impactData )
-{
- // generic shield ping FX
- Vortex_SpawnShieldPingFX( vortexWeapon, impactData )
-
- // specific absorb FX
- impactData.fxEnt_absorb = Vortex_SpawnImpactAbsorbFX( vortexWeapon, impactData )
-}
-
-// FX played when something first enters the vortex sphere
-function Vortex_SpawnShieldPingFX( entity vortexWeapon, impactData )
-{
- entity player = vortexWeapon.GetWeaponOwner()
- Assert( player )
-
- local absorbSFX = impactData.absorbSFX
- //printt( "SFX absorb sound:", absorbSFX )
- if ( vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- EmitSoundOnEntity( vortexWeapon, "Vortex_Shield_Deflect_Amped" )
- else
- {
- EmitSoundOnEntity( vortexWeapon, absorbSFX )
- if ( impactData.absorbSFX_1p_vs_3p != null )
- {
- if ( IsValid( impactData.attacker ) && impactData.attacker.IsPlayer() )
- {
- EmitSoundOnEntityOnlyToPlayer( vortexWeapon, impactData.attacker, impactData.absorbSFX_1p_vs_3p )
- }
- }
- }
-
- entity pingFX = CreateEntity( "info_particle_system" )
-
- if ( vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- {
- if ( "fxBulletHitBurn" in vortexWeapon.s )
- pingFX.SetValueForEffectNameKey( expect asset( vortexWeapon.s.fxBulletHitBurn ) )
- }
- else
- {
- if ( "fxBulletHit" in vortexWeapon.s )
- pingFX.SetValueForEffectNameKey( expect asset( vortexWeapon.s.fxBulletHit ) )
- }
-
- pingFX.kv.start_active = 1
-
- DispatchSpawn( pingFX )
-
- pingFX.SetOrigin( impactData.origin )
- pingFX.SetParent( player )
- pingFX.Kill_Deprecated_UseDestroyInstead( 0.25 )
-}
-
-function Vortex_SpawnHeatShieldPingFX( entity vortexWeapon, impactData, bool impactTypeIsBullet )
-{
- entity player = vortexWeapon.GetWeaponOwner()
- Assert( player )
-
- if ( impactTypeIsBullet )
- EmitSoundOnEntity( vortexWeapon, "heat_shield_stop_bullet" )
- else
- EmitSoundOnEntity( vortexWeapon, "heat_shield_stop_projectile" )
-
- entity pingFX = CreateEntity( "info_particle_system" )
-
- if ( "fxBulletHit" in vortexWeapon.s )
- pingFX.SetValueForEffectNameKey( expect asset( vortexWeapon.s.fxBulletHit ) )
-
- pingFX.kv.start_active = 1
-
- DispatchSpawn( pingFX )
-
- pingFX.SetOrigin( impactData.origin )
- pingFX.SetParent( player )
- pingFX.Kill_Deprecated_UseDestroyInstead( 0.25 )
-}
-
-function Vortex_SpawnImpactAbsorbFX( entity vortexWeapon, impactData )
-{
- // in case we're in the middle of cleaning the weapon up
- if ( !IsValid( vortexWeapon.s.vortexBulletEffectCP ) )
- return
-
- entity owner = vortexWeapon.GetWeaponOwner()
- Assert( owner )
-
- local fxRefs = []
-
- // owner
- {
- entity fxRef = CreateEntity( "info_particle_system" )
-
- fxRef.SetValueForEffectNameKey( expect asset( impactData.absorbFX ) )
- fxRef.kv.start_active = 1
- fxRef.SetStopType( "destroyImmediately" )
- fxRef.kv.VisibilityFlags = ENTITY_VISIBLE_TO_OWNER
- fxRef.kv.cpoint1 = vortexWeapon.s.vortexBulletEffectCP.GetTargetName()
-
- DispatchSpawn( fxRef )
-
- fxRef.SetOwner( owner )
- fxRef.SetOrigin( impactData.origin )
- fxRef.SetParent( owner )
-
- fxRefs.append( fxRef )
- }
-
- // everyone else
- {
- entity fxRef = CreateEntity( "info_particle_system" )
-
- fxRef.SetValueForEffectNameKey( expect asset( impactData.absorbFX_3p ) )
- fxRef.kv.start_active = 1
- fxRef.SetStopType( "destroyImmediately" )
- fxRef.kv.VisibilityFlags = (ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY) // other only visibility
- fxRef.kv.cpoint1 = vortexWeapon.s.vortexBulletEffectCP.GetTargetName()
-
- DispatchSpawn( fxRef )
-
- fxRef.SetOwner( owner )
- fxRef.SetOrigin( impactData.origin )
- fxRef.SetParent( owner )
-
- fxRefs.append( fxRef )
- }
-
- return fxRefs
-}
-
-function Vortex_CleanupImpactAbsorbFX( entity vortexWeapon )
-{
- foreach ( impactData in Vortex_GetAllImpactEvents( vortexWeapon ) )
- {
- Vortex_ImpactData_KillAbsorbFX( impactData )
- }
-}
-
-function Vortex_ImpactData_KillAbsorbFX( impactData )
-{
- foreach ( fxRef in impactData.fxEnt_absorb )
- {
- if ( !IsValid( fxRef ) )
- continue
-
- fxRef.Fire( "DestroyImmediately" )
- fxRef.Kill_Deprecated_UseDestroyInstead()
- }
-}
-
-bool function PlayerDiedOrDisconnected( entity player )
-{
- if ( !IsValid( player ) )
- return true
-
- if ( !IsAlive( player ) )
- return true
-
- if ( IsDisconnected( player ) )
- return true
-
- return false
-}
-
-#endif // SERVER
-
-int function VortexPrimaryAttack( entity vortexWeapon, WeaponPrimaryAttackParams attackParams )
-{
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
- if ( !vortexSphere )
- return 0
-
- #if SERVER
- Assert( vortexSphere )
- #endif
-
- int totalfired = 0
- int totalAttempts = 0
-
- bool forceReleased = false
- // in this case, it's also considered "force released" if the charge time runs out
- if ( vortexWeapon.IsForceRelease() || vortexWeapon.GetWeaponChargeFraction() == 1 )
- forceReleased = true
-
- // PREDICTED REFIRES
- // bullet impact events don't individually fire back per event because we aggregate and then shotgun blast them
- int bulletsFired = Vortex_FireBackBullets( vortexWeapon, attackParams )
- totalfired += bulletsFired
-
- // UNPREDICTED REFIRES
- #if SERVER
- //printt( "server: force released?", forceReleased )
-
- local unpredictedRefires = Vortex_GetProjectileImpacts( vortexWeapon )
-
- // HACK we don't actually want to refire them with a spiral but
- // this is to temporarily ensure compatibility with the Titan rocket launcher
- if ( !( "spiralMissileIdx" in vortexWeapon.s ) )
- vortexWeapon.s.spiralMissileIdx <- null
- vortexWeapon.s.spiralMissileIdx = 0
-
- foreach ( impactData in unpredictedRefires )
- {
- table fakeAttackParams = {pos = attackParams.pos, dir = attackParams.dir, firstTimePredicted = attackParams.firstTimePredicted, burstIndex = attackParams.burstIndex}
- bool didFire = DoVortexAttackForImpactData( vortexWeapon, fakeAttackParams, impactData, totalAttempts )
- if ( didFire )
- totalfired++
- totalAttempts++
- }
- //printt( "totalfired", totalfired )
- #else
- totalfired += GetProjectilesAbsorbedCount( vortexWeapon )
- #endif
-
- SetVortexAmmo( vortexWeapon, 0 )
-
- vortexWeapon.Signal( "VortexFired" )
-
- if ( forceReleased )
- DestroyVortexSphereFromVortexWeapon( vortexWeapon )
- else
- DisableVortexSphereFromVortexWeapon( vortexWeapon )
-
- return totalfired
-}
-
-int function Vortex_FireBackBullets( entity vortexWeapon, WeaponPrimaryAttackParams attackParams )
-{
- int bulletCount = GetBulletsAbsorbedCount( vortexWeapon )
- //Defensive Check - Couldn't repro error.
- if ( "shotgunPelletsToIgnore" in vortexWeapon.s )
- bulletCount = int( ceil( bulletCount - vortexWeapon.s.shotgunPelletsToIgnore ) )
-
- if ( bulletCount )
- {
- bulletCount = minint( bulletCount, MAX_BULLET_PER_SHOT )
-
- //if ( IsClient() && GetLocalViewPlayer() == vortexWeapon.GetWeaponOwner() )
- // printt( "vortex firing", bulletCount, "bullets" )
-
- float radius = LOUD_WEAPON_AI_SOUND_RADIUS_MP;
- vortexWeapon.EmitWeaponNpcSound( radius, 0.2 )
- int damageType = damageTypes.shotgun | DF_VORTEX_REFIRE
- if ( bulletCount == 1 )
- vortexWeapon.FireWeaponBullet( attackParams.pos, attackParams.dir, bulletCount, damageType )
- else
- ShotgunBlast( vortexWeapon, attackParams.pos, attackParams.dir, bulletCount, damageType )
- }
-
- return bulletCount
-}
-
-#if SERVER
-bool function Vortex_FireBackExplosiveRound( vortexWeapon, attackParams, impactData, sequenceID )
-{
- expect entity( vortexWeapon )
-
- // common projectile data
- float projSpeed = 8000.0
- int damageType = damageTypes.explosive | DF_VORTEX_REFIRE
-
- vortexWeapon.EmitWeaponSound( "Weapon.Explosion_Med" )
-
- vector attackPos
- //Requires code feature to properly fire tracers from offset positions.
- //if ( vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- // attackPos = impactData.origin
- //else
- attackPos = Vortex_GenerateRandomRefireOrigin( vortexWeapon )
-
- vector fireVec = Vortex_GenerateRandomRefireVector( vortexWeapon, VORTEX_EXP_ROUNDS_RETURN_SPREAD_XY, VORTEX_EXP_ROUNDS_RETURN_SPREAD_Z )
-
- // fire off the bolt
- entity bolt = vortexWeapon.FireWeaponBolt( attackPos, fireVec, projSpeed, damageType, damageType, PROJECTILE_NOT_PREDICTED, sequenceID )
- if ( bolt )
- {
- bolt.kv.gravity = 0.3
-
- Vortex_ProjectileCommonSetup( bolt, impactData )
- }
-
- return true
-}
-
-bool function Vortex_FireBackProjectileBullet( vortexWeapon, attackParams, impactData, sequenceID )
-{
- expect entity( vortexWeapon )
-
- // common projectile data
- float projSpeed = 12000.0
- int damageType = damageTypes.bullet | DF_VORTEX_REFIRE
-
- vortexWeapon.EmitWeaponSound( "Weapon.Explosion_Med" )
-
- vector attackPos
- //Requires code feature to properly fire tracers from offset positions.
- //if ( vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- // attackPos = impactData.origin
- //else
- attackPos = Vortex_GenerateRandomRefireOrigin( vortexWeapon )
-
- vector fireVec = Vortex_GenerateRandomRefireVector( vortexWeapon, 0.15, 0.1 )
- //printt( Time(), fireVec ) // print for bug with random
-
- // fire off the bolt
- entity bolt = vortexWeapon.FireWeaponBolt( attackPos, fireVec, projSpeed, damageType, damageType, PROJECTILE_NOT_PREDICTED, sequenceID )
- if ( bolt )
- {
- bolt.kv.gravity = 0.0
-
- Vortex_ProjectileCommonSetup( bolt, impactData )
- }
-
- return true
-}
-
-vector function Vortex_GenerateRandomRefireOrigin( entity vortexWeapon, float distFromCenter = 3.0 )
-{
- float distFromCenter_neg = distFromCenter * -1
-
- vector attackPos = expect vector( vortexWeapon.s.vortexBulletEffectCP.GetOrigin() )
-
- float x = RandomFloatRange( distFromCenter_neg, distFromCenter )
- float y = RandomFloatRange( distFromCenter_neg, distFromCenter )
- float z = RandomFloatRange( distFromCenter_neg, distFromCenter )
-
- attackPos = attackPos + Vector( x, y, z )
-
- return attackPos
-}
-
-vector function Vortex_GenerateRandomRefireVector( entity vortexWeapon, float vecSpread, float vecSpreadZ )
-{
- float x = RandomFloatRange( vecSpread * -1, vecSpread )
- float y = RandomFloatRange( vecSpread * -1, vecSpread )
- float z = RandomFloatRange( vecSpreadZ * -1, vecSpreadZ )
-
- vector fireVec = vortexWeapon.GetWeaponOwner().GetPlayerOrNPCViewVector() + Vector( x, y, z )
- return fireVec
-}
-
-bool function Vortex_FireBackRocket( vortexWeapon, attackParams, impactData, sequenceID )
-{
- expect entity( vortexWeapon )
-
- // TODO prediction for clients
- Assert( IsServer() )
-
- entity rocket = vortexWeapon.FireWeaponMissile( attackParams.pos, attackParams.dir, 1800.0, damageTypes.largeCaliberExp | DF_VORTEX_REFIRE, damageTypes.largeCaliberExp | DF_VORTEX_REFIRE, false, PROJECTILE_NOT_PREDICTED )
-
- if ( rocket )
- {
- rocket.kv.lifetime = RandomFloatRange( 2.6, 3.5 )
-
- InitMissileForRandomDriftForVortexLow( rocket, expect vector( attackParams.pos ), expect vector( attackParams.dir ) )
-
- Vortex_ProjectileCommonSetup( rocket, impactData )
- }
-
- return true
-}
-
-bool function Vortex_FireBackGrenade( entity vortexWeapon, attackParams, impactData, int attackSeedCount, float baseFuseTime )
-{
- float x = RandomFloatRange( -0.2, 0.2 )
- float y = RandomFloatRange( -0.2, 0.2 )
- float z = RandomFloatRange( -0.2, 0.2 )
-
- vector velocity = ( expect vector( attackParams.dir ) + Vector( x, y, z ) ) * 1500
- vector angularVelocity = Vector( RandomFloatRange( -1200, 1200 ), 100, 0 )
-
- bool hasIgnitionTime = vortexImpactWeaponInfo[ impactData.weaponName ].grenade_ignition_time > 0
- float fuseTime = hasIgnitionTime ? 0.0 : baseFuseTime
- const int HARDCODED_DAMAGE_TYPE = (damageTypes.explosive | DF_VORTEX_REFIRE)
-
- entity grenade = vortexWeapon.FireWeaponGrenade( attackParams.pos, velocity, angularVelocity, fuseTime, HARDCODED_DAMAGE_TYPE, HARDCODED_DAMAGE_TYPE, PROJECTILE_NOT_PREDICTED, true, true )
- if ( grenade )
- {
- Grenade_Init( grenade, vortexWeapon )
- Vortex_ProjectileCommonSetup( grenade, impactData )
- if ( hasIgnitionTime )
- grenade.SetGrenadeIgnitionDuration( vortexImpactWeaponInfo[ impactData.weaponName ].grenade_ignition_time )
- }
-
- return (grenade ? true : false)
-}
-
-bool function DoVortexAttackForImpactData( entity vortexWeapon, attackParams, impactData, int attackSeedCount )
-{
- bool didFire = false
- switch ( impactData.refireBehavior )
- {
- case VORTEX_REFIRE_EXPLOSIVE_ROUND:
- didFire = Vortex_FireBackExplosiveRound( vortexWeapon, attackParams, impactData, attackSeedCount )
- break
-
- case VORTEX_REFIRE_ROCKET:
- didFire = Vortex_FireBackRocket( vortexWeapon, attackParams, impactData, attackSeedCount )
- break
-
- case VORTEX_REFIRE_GRENADE:
- didFire = Vortex_FireBackGrenade( vortexWeapon, attackParams, impactData, attackSeedCount, 1.25 )
- break
-
- case VORTEX_REFIRE_GRENADE_LONG_FUSE:
- didFire = Vortex_FireBackGrenade( vortexWeapon, attackParams, impactData, attackSeedCount, 10.0 )
- break
-
- case VORTEX_REFIRE_BULLET:
- didFire = Vortex_FireBackProjectileBullet( vortexWeapon, attackParams, impactData, attackSeedCount )
- break
-
- case VORTEX_REFIRE_NONE:
- break
- }
-
- return didFire
-}
-
-function Vortex_ProjectileCommonSetup( entity projectile, impactData )
-{
- // custom tag it so it shows up correctly if it hits another vortex sphere
- projectile.s.originalDamageSource <- impactData.damageSourceID
-
- Vortex_SetImpactEffectTable_OnProjectile( projectile, impactData ) // set the correct impact effect table
-
- projectile.SetVortexRefired( true ) // This tells code the projectile was refired from the vortex so that it uses "projectile_vortex_vscript"
- projectile.SetModel( GetWeaponInfoFileKeyFieldAsset_Global( impactData.weaponName, "projectilemodel" ) )
- projectile.SetWeaponClassName( impactData.weaponName ) // causes the projectile to use its normal trail FX
-
- projectile.ProjectileSetDamageSourceID( impactData.damageSourceID ) // obit will show the owner weapon
-}
-
-// gives a refired projectile the correct impact effect table
-function Vortex_SetImpactEffectTable_OnProjectile( projectile, impactData )
-{
- //Getting more info for bug 207595, don't check into Staging.
- #if DEV
- printt( "impactData.impact_effect_table ", impactData.impact_effect_table )
- if ( impactData.impact_effect_table == "" )
- PrintTable( impactData )
- #endif
-
- local fxTableHandle = GetImpactEffectTable( impactData.impact_effect_table )
-
- projectile.SetImpactEffectTable( fxTableHandle )
-}
-#endif // SERVER
-
-// absorbed bullets are tracked with a special networked kv variable because clients need to know how many bullets to fire as well, when they are doing the client version of FireWeaponBullet
-int function GetBulletsAbsorbedCount( entity vortexWeapon )
-{
- if ( !vortexWeapon )
- return 0
-
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
- if ( !vortexSphere )
- return 0
-
- return vortexSphere.GetBulletAbsorbedCount()
-}
-
-int function GetProjectilesAbsorbedCount( entity vortexWeapon )
-{
- if ( !vortexWeapon )
- return 0
-
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
- if ( !vortexSphere )
- return 0
-
- return vortexSphere.GetProjectileAbsorbedCount()
-}
-
-#if SERVER
-function Vortex_GetProjectileImpacts( entity vortexWeapon )
-{
- local impacts = []
- foreach ( impactData in Vortex_GetAllImpactEvents( vortexWeapon ) )
- {
- if ( impactData.impactType == "projectile" )
- impacts.append( impactData )
- }
-
- return impacts
-}
-
-function Vortex_GetHitscanBulletImpacts( entity vortexWeapon )
-{
- local impacts = []
- foreach ( impactData in Vortex_GetAllImpactEvents( vortexWeapon ) )
- {
- if ( impactData.impactType == "hitscan" )
- impacts.append( impactData )
- }
-
- return impacts
-}
-
-int function GetHitscanBulletImpactCount( entity vortexWeapon )
-{
- int count = 0
- foreach ( impactData in Vortex_GetAllImpactEvents( vortexWeapon ) )
- {
- if ( impactData.impactType == "hitscan" )
- count++
- }
-
- return count
-}
-#endif // SERVER
-
-// // lets the damage callback communicate to the attacker that he hit a vortex shield
-function Vortex_NotifyAttackerDidDamage( entity attacker, entity vortexOwner, hitPos )
-{
- if ( !IsValid( attacker ) || !attacker.IsPlayer() )
- return
-
- if ( !IsValid( vortexOwner ) )
- return
-
- Assert( hitPos )
-
- attacker.NotifyDidDamage( vortexOwner, 0, hitPos, 0, 0, DAMAGEFLAG_VICTIM_HAS_VORTEX, 0, null, 0 )
-}
-
-function SetVortexAmmo( entity vortexWeapon, count )
-{
- entity owner = vortexWeapon.GetWeaponOwner()
- if ( !IsValid_ThisFrame( owner ) )
- return
- #if CLIENT
- if ( !IsLocalViewPlayer( owner ) )
- return
- #endif
-
- vortexWeapon.SetWeaponPrimaryAmmoCount( count )
-}
-
-
-// sets the RGB color value for the vortex sphere FX based on current charge fraction
-function VortexSphereColorUpdate( entity weapon, sphereClientFXHandle = null )
-{
- weapon.EndSignal( "VortexStopping" )
-
- #if CLIENT
- Assert( sphereClientFXHandle != null )
- #endif
- bool isIonVortex = weapon.GetWeaponClassName() == "mp_titanweapon_vortex_shield_ion"
- entity weaponOwner = weapon.GetWeaponOwner()
- float energyTotal = float ( weaponOwner.GetSharedEnergyTotal() )
- while( IsValid( weapon ) && IsValid( weaponOwner ) )
- {
- vector colorVec
- if ( isIonVortex )
- {
- float energyFrac = 1.0 - float( weaponOwner.GetSharedEnergyCount() ) / energyTotal
- if ( weapon.HasMod( "pas_ion_vortex" ) )
- colorVec = GetVortexSphereCurrentColor( energyFrac, VORTEX_SPHERE_COLOR_PAS_ION_VORTEX )
- else
- colorVec = GetVortexSphereCurrentColor( energyFrac )
- }
- else
- {
- colorVec = GetVortexSphereCurrentColor( weapon.GetWeaponChargeFraction() )
- }
-
-
- // update the world entity that is linked to the world FX playing on the server
- #if SERVER
- weapon.s.vortexSphereColorCP.SetOrigin( colorVec )
- #else
- // handles the server killing the vortex sphere without the client knowing right away,
- // for example if an explosive goes off and we short circuit the charge timer
- if ( !EffectDoesExist( sphereClientFXHandle ) )
- break
-
- EffectSetControlPointVector( sphereClientFXHandle, 1, colorVec )
- #endif
-
- WaitFrame()
- }
-}
-
-vector function GetVortexSphereCurrentColor( float chargeFrac, vector fullHealthColor = VORTEX_SPHERE_COLOR_CHARGE_FULL )
-{
- return GetTriLerpColor( chargeFrac, fullHealthColor, VORTEX_SPHERE_COLOR_CHARGE_MED, VORTEX_SPHERE_COLOR_CHARGE_EMPTY )
-}
-
-vector function GetShieldTriLerpColor( float frac )
-{
- return GetTriLerpColor( frac, VORTEX_SPHERE_COLOR_CHARGE_FULL, VORTEX_SPHERE_COLOR_CHARGE_MED, VORTEX_SPHERE_COLOR_CHARGE_EMPTY )
-}
-
-vector function GetTriLerpColor( float fraction, vector color1, vector color2, vector color3 )
-{
- float crossover1 = VORTEX_SPHERE_COLOR_CROSSOVERFRAC_FULL2MED // from zero to this fraction, fade between color1 and color2
- float crossover2 = VORTEX_SPHERE_COLOR_CROSSOVERFRAC_MED2EMPTY // from crossover1 to this fraction, fade between color2 and color3
-
- float r, g, b
-
- // 0 = full charge, 1 = no charge remaining
- if ( fraction < crossover1 )
- {
- r = Graph( fraction, 0, crossover1, color1.x, color2.x )
- g = Graph( fraction, 0, crossover1, color1.y, color2.y )
- b = Graph( fraction, 0, crossover1, color1.z, color2.z )
- return <r, g, b>
- }
- else if ( fraction < crossover2 )
- {
- r = Graph( fraction, crossover1, crossover2, color2.x, color3.x )
- g = Graph( fraction, crossover1, crossover2, color2.y, color3.y )
- b = Graph( fraction, crossover1, crossover2, color2.z, color3.z )
- return <r, g, b>
- }
- else
- {
- // for the last bit of overload timer, keep it max danger color
- r = color3.x
- g = color3.y
- b = color3.z
- return <r, g, b>
- }
-
- unreachable
-}
-
-// generic impact validation
-#if SERVER
-bool function ValidateVortexImpact( entity vortexSphere, entity projectile = null )
-{
- Assert( IsServer() )
-
- if ( !IsValid( vortexSphere ) )
- return false
-
- if ( !vortexSphere.GetOwnerWeapon() )
- return false
-
- entity vortexWeapon = vortexSphere.GetOwnerWeapon()
- if ( !IsValid( vortexWeapon ) )
- return false
-
- if ( projectile )
- {
- if ( !IsValid_ThisFrame( projectile ) )
- return false
-
- if ( projectile.ProjectileGetWeaponInfoFileKeyField( "projectile_ignores_vortex" ) == 1 )
- return false
-
- if ( projectile.ProjectileGetWeaponClassName() == "" )
- return false
-
- // TEMP HACK
- if ( projectile.ProjectileGetWeaponClassName() == "mp_weapon_tether" )
- return false
- }
-
- return true
-}
-#endif
-
-/********************************/
-/* Setting override functions */
-/********************************/
-
-function Vortex_SetTagName( entity weapon, string tagName )
-{
- Vortex_SetWeaponSettingOverride( weapon, "vortexTagName", tagName )
-}
-
-function Vortex_SetBulletCollectionOffset( entity weapon, vector offset )
-{
- Vortex_SetWeaponSettingOverride( weapon, "bulletCollectionOffset", offset )
-}
-
-function Vortex_SetWeaponSettingOverride( entity weapon, string setting, value )
-{
- if ( !( setting in weapon.s ) )
- weapon.s[ setting ] <- null
- weapon.s[ setting ] = value
-}
-
-string function GetVortexTagName( entity weapon )
-{
- if ( "vortexTagName" in weapon.s )
- return expect string( weapon.s.vortexTagName )
-
- return "vortex_center"
-}
-
-vector function GetBulletCollectionOffset( entity weapon )
-{
- if ( "bulletCollectionOffset" in weapon.s )
- return expect vector( weapon.s.bulletCollectionOffset )
-
- entity owner = weapon.GetWeaponOwner()
- if ( owner.IsTitan() )
- return Vector( 300.0, -90.0, -70.0 )
- else
- return Vector( 80.0, 17.0, -11.0 )
-
- unreachable
-}
-
-
-#if SERVER
-function VortexSphereDrainHealthForDamage( entity vortexSphere, damage )
-{
- // don't drain the health of vortex_spheres that are set to be invulnerable. This is the case for the Particle Wall
- if ( vortexSphere.IsInvulnerable() )
- return
-
- local result = {}
- result.damage <- damage
- vortexSphere.Signal( "Script_OnDamaged", result )
-
- int currentHealth = vortexSphere.GetHealth()
- Assert( damage >= 0 )
- // JFS to fix phone home bug; we never hit the assert above locally...
- damage = max( damage, 0 )
- vortexSphere.SetHealth( currentHealth - damage )
-
- entity vortexWeapon = vortexSphere.GetOwnerWeapon()
- if ( IsValid( vortexWeapon ) && vortexWeapon.HasMod( "fd_gun_shield_redirect" ) )
- {
- entity owner = vortexWeapon.GetWeaponOwner()
- if ( IsValid( owner ) && owner.IsTitan() )
- {
- entity soul = owner.GetTitanSoul()
- if ( IsValid( soul ) )
- {
- int shieldRestoreAmount = int( damage ) //Might need tuning
- soul.SetShieldHealth( min( soul.GetShieldHealth() + shieldRestoreAmount, soul.GetShieldHealthMax() ) )
- }
- }
- }
-
- UpdateShieldWallColorForFrac( vortexSphere.e.shieldWallFX, GetHealthFrac( vortexSphere ) )
-}
-#endif
-
-
-bool function CodeCallback_OnVortexHitBullet( entity weapon, entity vortexSphere, var damageInfo )
-{
- bool isAmpedWall = vortexSphere.GetTargetName() == PROTO_AMPED_WALL
- bool takesDamage = !isAmpedWall
- bool adjustImpactAngles = !(vortexSphere.GetTargetName() == GUN_SHIELD_WALL)
-
- #if SERVER
- if ( vortexSphere.e.BulletHitRules != null )
- {
- vortexSphere.e.BulletHitRules( vortexSphere, damageInfo )
- takesDamage = takesDamage && (DamageInfo_GetDamage( damageInfo ) > 0)
- }
- #endif
-
- vector damageAngles = vortexSphere.GetAngles()
-
- if ( adjustImpactAngles )
- damageAngles = AnglesCompose( damageAngles, Vector( 90, 0, 0 ) )
-
- int teamNum = vortexSphere.GetTeam()
-
- #if CLIENT
- vector damageOrigin = DamageInfo_GetDamagePosition( damageInfo )
- if ( !isAmpedWall )
- {
- // TODO: slightly change angles to match radius rotation of vortex cylinder
- int effectHandle = StartParticleEffectInWorldWithHandle( GetParticleSystemIndex( SHIELD_WALL_BULLET_FX ), damageOrigin, damageAngles )
- //local color = GetShieldTriLerpColor( 1 - GetHealthFrac( vortexSphere ) )
- vector color = GetShieldTriLerpColor( 0.0 )
- EffectSetControlPointVector( effectHandle, 1, color )
- }
-
- if ( takesDamage )
- {
- float damage = ceil( DamageInfo_GetDamage( damageInfo ) )
- int damageType = DamageInfo_GetCustomDamageType( damageInfo )
- DamageFlyout( damage, damageOrigin, vortexSphere, false, false )
- }
-
- if ( DamageInfo_GetAttacker( damageInfo ) && DamageInfo_GetAttacker( damageInfo ).IsTitan() )
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Heavy.BulletImpact_1P_vs_3P" )
- else
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Light.BulletImpact_1P_vs_3P" )
- #else
- if ( !isAmpedWall )
- {
- int fxId = GetParticleSystemIndex( SHIELD_WALL_BULLET_FX )
- PlayEffectOnVortexSphere( fxId, DamageInfo_GetDamagePosition( damageInfo ), damageAngles, vortexSphere )
- }
-
- entity weapon = DamageInfo_GetWeapon( damageInfo )
- float damage = ceil( DamageInfo_GetDamage( damageInfo ) )
-
- Assert( damage >= 0, "Bug 159851 - Damage should be greater than or equal to 0.")
- damage = max( 0.0, damage )
-
- if ( IsValid( weapon ) )
- damage = HandleWeakToPilotWeapons( vortexSphere, weapon.GetWeaponClassName(), damage )
-
- if ( takesDamage )
- {
- //JFS - Arc Round bug fix for Monarch. Projectiles vortex callback doesn't even have damageInfo, so the shield modifier here doesn't exist in VortexSphereDrainHealthForDamage like it should.
- ShieldDamageModifier damageModifier = GetShieldDamageModifier( damageInfo )
- damage *= damageModifier.damageScale
- VortexSphereDrainHealthForDamage( vortexSphere, damage )
- }
-
- if ( DamageInfo_GetAttacker( damageInfo ) && DamageInfo_GetAttacker( damageInfo ).IsTitan() )
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Heavy.BulletImpact_3P_vs_3P" )
- else
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Light.BulletImpact_3P_vs_3P" )
- #endif
-
- if ( isAmpedWall )
- {
- #if SERVER
- DamageInfo_ScaleDamage( damageInfo, AMPED_DAMAGE_SCALAR )
- #endif
- return false
- }
-
- return true
-}
-
-bool function OnVortexHitBullet_BubbleShieldNPC( entity vortexSphere, var damageInfo )
-{
- vector vortexOrigin = vortexSphere.GetOrigin()
- vector damageOrigin = DamageInfo_GetDamagePosition( damageInfo )
-
- float distSq = DistanceSqr( vortexOrigin, damageOrigin )
- if ( distSq < MINION_BUBBLE_SHIELD_RADIUS_SQR )
- return false//the damage is coming from INSIDE the sphere
-
- vector damageVec = damageOrigin - vortexOrigin
- vector damageAngles = VectorToAngles( damageVec )
- damageAngles = AnglesCompose( damageAngles, Vector( 90, 0, 0 ) )
-
- int teamNum = vortexSphere.GetTeam()
-
- #if CLIENT
- int effectHandle = StartParticleEffectInWorldWithHandle( GetParticleSystemIndex( SHIELD_WALL_BULLET_FX ), damageOrigin, damageAngles )
-
- vector color = GetShieldTriLerpColor( 0.9 )
- EffectSetControlPointVector( effectHandle, 1, color )
-
- if ( DamageInfo_GetAttacker( damageInfo ) && DamageInfo_GetAttacker( damageInfo ).IsTitan() )
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Heavy.BulletImpact_1P_vs_3P" )
- else
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Light.BulletImpact_1P_vs_3P" )
- #else
- int fxId = GetParticleSystemIndex( SHIELD_WALL_BULLET_FX )
- PlayEffectOnVortexSphere( fxId, DamageInfo_GetDamagePosition( damageInfo ), damageAngles, vortexSphere )
- //VortexSphereDrainHealthForDamage( vortexSphere, DamageInfo_GetWeapon( damageInfo ), null )
-
- if ( DamageInfo_GetAttacker( damageInfo ) && DamageInfo_GetAttacker( damageInfo ).IsTitan() )
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Heavy.BulletImpact_3P_vs_3P" )
- else
- EmitSoundAtPosition( teamNum, DamageInfo_GetDamagePosition( damageInfo ), "TitanShieldWall.Light.BulletImpact_3P_vs_3P" )
- #endif
- return true
-}
-
-bool function CodeCallback_OnVortexHitProjectile( entity weapon, entity vortexSphere, entity attacker, entity projectile, vector contactPos )
-{
- // code shouldn't call this on an invalid vortexsphere!
- if ( !IsValid( vortexSphere ) )
- return false
-
- var ignoreVortex = projectile.ProjectileGetWeaponInfoFileKeyField( "projectile_ignores_vortex" )
- if ( ignoreVortex != null )
- {
- #if SERVER
- if ( projectile.proj.hasBouncedOffVortex )
- return false
-
- vector velocity = projectile.GetVelocity()
- vector multiplier
-
- switch ( ignoreVortex )
- {
- case "drop":
- multiplier = < -0.25, -0.25, 0.0 >
- break
-
- case "fall_vortex":
- case "fall":
- multiplier = < -0.25, -0.25, -0.25 >
- break
-
- case "mirror":
- // bounce back, assume along xy axis
- multiplier = < -1.0, -1.0, 1.0 >
- break
-
- default:
- CodeWarning( "Unknown projectile_ignores_vortex " + ignoreVortex )
- break
- }
-
- velocity = < velocity.x * multiplier.x, velocity.y * multiplier.y, velocity.z * multiplier.z >
- projectile.proj.hasBouncedOffVortex = true
- projectile.SetVelocity( velocity )
- #endif
- return false
- }
-
- bool adjustImpactAngles = !(vortexSphere.GetTargetName() == GUN_SHIELD_WALL)
-
- vector damageAngles = vortexSphere.GetAngles()
-
- if ( adjustImpactAngles )
- damageAngles = AnglesCompose( damageAngles, Vector( 90, 0, 0 ) )
-
- asset projectileSettingFX = projectile.GetProjectileWeaponSettingAsset( eWeaponVar.vortex_impact_effect )
- asset impactFX = (projectileSettingFX != $"") ? projectileSettingFX : SHIELD_WALL_EXPMED_FX
-
- bool isAmpedWall = vortexSphere.GetTargetName() == PROTO_AMPED_WALL
- bool takesDamage = !isAmpedWall
-
- #if SERVER
- if ( vortexSphere.e.ProjectileHitRules != null )
- takesDamage = vortexSphere.e.ProjectileHitRules( vortexSphere, attacker, takesDamage )
- #endif
- // hack to let client know about amped wall, and to amp the shot
- if ( isAmpedWall )
- impactFX = AMPED_WALL_IMPACT_FX
-
- int teamNum = vortexSphere.GetTeam()
-
- #if CLIENT
- if ( !isAmpedWall )
- {
- int effectHandle = StartParticleEffectInWorldWithHandle( GetParticleSystemIndex( impactFX ), contactPos, damageAngles )
- //local color = GetShieldTriLerpColor( 1 - GetHealthFrac( vortexSphere ) )
- vector color = GetShieldTriLerpColor( 0.0 )
- EffectSetControlPointVector( effectHandle, 1, color )
- }
-
- var impact_sound_1p = projectile.ProjectileGetWeaponInfoFileKeyField( "vortex_impact_sound_1p" )
- if ( impact_sound_1p == null )
- impact_sound_1p = "TitanShieldWall.Explosive.BulletImpact_1P_vs_3P"
-
- EmitSoundAtPosition( teamNum, contactPos, impact_sound_1p )
- #else
- if ( !isAmpedWall )
- {
- int fxId = GetParticleSystemIndex( impactFX )
- PlayEffectOnVortexSphere( fxId, contactPos, damageAngles, vortexSphere )
- }
-
- float damage = float( projectile.GetProjectileWeaponSettingInt( eWeaponVar.damage_near_value ) )
- // once damageInfo is passed correctly we'll use that instead of looking up the values from the weapon .txt file.
- // local damage = ceil( DamageInfo_GetDamage( damageInfo ) )
-
- damage = HandleWeakToPilotWeapons( vortexSphere, projectile.ProjectileGetWeaponClassName(), damage )
- damage = damage + CalculateTitanSniperExtraDamage( projectile, vortexSphere )
-
- if ( takesDamage )
- {
- VortexSphereDrainHealthForDamage( vortexSphere, damage )
- if ( IsValid( attacker ) && attacker.IsPlayer() )
- attacker.NotifyDidDamage( vortexSphere, 0, contactPos, 0, damage, DF_NO_HITBEEP, 0, null, 0 )
- }
-
- var impact_sound_3p = projectile.ProjectileGetWeaponInfoFileKeyField( "vortex_impact_sound_3p" )
-
- if ( impact_sound_3p == null )
- impact_sound_3p = "TitanShieldWall.Explosive.BulletImpact_3P_vs_3P"
-
- EmitSoundAtPosition( teamNum, contactPos, impact_sound_3p )
-
- int damageSourceID = projectile.ProjectileGetDamageSourceID()
- switch ( damageSourceID )
- {
- case eDamageSourceId.mp_titanweapon_dumbfire_rockets:
- vector normal = projectile.GetVelocity() * -1
- normal = Normalize( normal )
- ClusterRocket_Detonate( projectile, normal )
- CreateNoSpawnArea( TEAM_INVALID, TEAM_INVALID, contactPos, ( CLUSTER_ROCKET_BURST_COUNT / 5.0 ) * 0.5 + 1.0, CLUSTER_ROCKET_BURST_RANGE + 100 )
- break
-
- case eDamageSourceId.mp_weapon_grenade_electric_smoke:
- ElectricGrenadeSmokescreen( projectile, FX_ELECTRIC_SMOKESCREEN_PILOT_AIR )
- break
-
- case eDamageSourceId.mp_weapon_grenade_emp:
-
- if ( StatusEffect_Get( vortexSphere, eStatusEffect.destroyed_by_emp ) )
- VortexSphereDrainHealthForDamage( vortexSphere, vortexSphere.GetHealth() )
- break
-
- case eDamageSourceId.mp_titanability_sonar_pulse:
- if ( IsValid( attacker ) && attacker.IsTitan() )
- {
- int team = attacker.GetTeam()
- PulseLocation( attacker, team, contactPos, false, false )
- array<string> mods = projectile.ProjectileGetMods()
- if ( mods.contains( "pas_tone_sonar" ) )
- thread DelayedPulseLocation( attacker, team, contactPos, false, false )
- }
- break
-
- }
- #endif
-
- // hack to let client know about amped wall, and to amp the shot
- if ( isAmpedWall )
- {
- #if SERVER
- projectile.proj.damageScale = AMPED_DAMAGE_SCALAR
- #endif
-
- return false
- }
-
- return true
-}
-
-bool function OnVortexHitProjectile_BubbleShieldNPC( entity vortexSphere, entity attacker, entity projectile, vector contactPos )
-{
- vector vortexOrigin = vortexSphere.GetOrigin()
-
- float dist = DistanceSqr( vortexOrigin, contactPos )
- if ( dist < MINION_BUBBLE_SHIELD_RADIUS_SQR )
- return false // the damage is coming from INSIDE THE SPHERE
-
- vector damageVec = Normalize( contactPos - vortexOrigin )
- vector damageAngles = VectorToAngles( damageVec )
- damageAngles = AnglesCompose( damageAngles, Vector( 90, 0, 0 ) )
-
- asset projectileSettingFX = projectile.GetProjectileWeaponSettingAsset( eWeaponVar.vortex_impact_effect )
- asset impactFX = (projectileSettingFX != $"") ? projectileSettingFX : SHIELD_WALL_EXPMED_FX
-
- int teamNum = vortexSphere.GetTeam()
-
- #if CLIENT
- int effectHandle = StartParticleEffectInWorldWithHandle( GetParticleSystemIndex( impactFX ), contactPos, damageAngles )
-
- vector color = GetShieldTriLerpColor( 0.9 )
- EffectSetControlPointVector( effectHandle, 1, color )
-
- EmitSoundAtPosition( teamNum, contactPos, "TitanShieldWall.Explosive.BulletImpact_1P_vs_3P" )
- #else
- int fxId = GetParticleSystemIndex( impactFX )
- PlayEffectOnVortexSphere( fxId, contactPos, damageAngles, vortexSphere )
-// VortexSphereDrainHealthForDamage( vortexSphere, null, projectile )
-
- EmitSoundAtPosition( teamNum, contactPos, "TitanShieldWall.Explosive.BulletImpact_3P_vs_3P" )
-
- if ( projectile.ProjectileGetDamageSourceID() == eDamageSourceId.mp_titanweapon_dumbfire_rockets )
- {
- vector normal = projectile.GetVelocity() * -1
- normal = Normalize( normal )
- ClusterRocket_Detonate( projectile, normal )
- CreateNoSpawnArea( TEAM_INVALID, TEAM_INVALID, contactPos, ( CLUSTER_ROCKET_BURST_COUNT / 5.0 ) * 0.5 + 1.0, CLUSTER_ROCKET_BURST_RANGE + 100 )
- }
- #endif
- return true
-}
-
-#if SERVER
-float function HandleWeakToPilotWeapons( entity vortexSphere, string weaponName, float damage )
-{
- if ( vortexSphere.e.proto_weakToPilotWeapons ) //needs code for real, but this is fine for prototyping
- {
- // is weapon a pilot weapon?
- local refType = GetWeaponInfoFileKeyField_Global( weaponName, "weaponClass" )
- if ( refType == "human" )
- {
- damage *= VORTEX_PILOT_WEAPON_WEAKNESS_DAMAGESCALE
- }
- }
-
- return damage
-}
-#endif
-
-// ???: reflectOrigin not used
-int function VortexReflectAttack( entity vortexWeapon, attackParams, vector reflectOrigin )
-{
- entity vortexSphere = vortexWeapon.GetWeaponUtilityEntity()
- if ( !vortexSphere )
- return 0
-
- #if SERVER
- Assert( vortexSphere )
- #endif
-
- int totalfired = 0
- int totalAttempts = 0
-
- bool forceReleased = false
- // in this case, it's also considered "force released" if the charge time runs out
- if ( vortexWeapon.IsForceRelease() || vortexWeapon.GetWeaponChargeFraction() == 1 )
- forceReleased = true
-
- //Requires code feature to properly fire tracers from offset positions.
- //if ( vortexWeapon.GetWeaponSettingBool( eWeaponVar.is_burn_mod ) )
- // attackParams.pos = reflectOrigin
-
- // PREDICTED REFIRES
- // bullet impact events don't individually fire back per event because we aggregate and then shotgun blast them
-
- //Remove the below script after FireWeaponBulletBroadcast
- //local bulletsFired = Vortex_FireBackBullets( vortexWeapon, attackParams )
- //totalfired += bulletsFired
- int bulletCount = GetBulletsAbsorbedCount( vortexWeapon )
- if ( bulletCount > 0 )
- {
- if ( "ampedBulletCount" in vortexWeapon.s )
- vortexWeapon.s.ampedBulletCount++
- else
- vortexWeapon.s.ampedBulletCount <- 1
- vortexWeapon.Signal( "FireAmpedVortexBullet" )
- totalfired += 1
- }
-
- // UNPREDICTED REFIRES
- #if SERVER
- //printt( "server: force released?", forceReleased )
-
- local unpredictedRefires = Vortex_GetProjectileImpacts( vortexWeapon )
-
- // HACK we don't actually want to refire them with a spiral but
- // this is to temporarily ensure compatibility with the Titan rocket launcher
- if ( !( "spiralMissileIdx" in vortexWeapon.s ) )
- vortexWeapon.s.spiralMissileIdx <- null
- vortexWeapon.s.spiralMissileIdx = 0
- foreach ( impactData in unpredictedRefires )
- {
- bool didFire = DoVortexAttackForImpactData( vortexWeapon, attackParams, impactData, totalAttempts )
- if ( didFire )
- totalfired++
- totalAttempts++
- }
- #endif
-
- SetVortexAmmo( vortexWeapon, 0 )
- vortexWeapon.Signal( "VortexFired" )
-
-#if SERVER
- vortexSphere.ClearAllBulletsFromSphere()
-#endif
-
- /*
- if ( forceReleased )
- DestroyVortexSphereFromVortexWeapon( vortexWeapon )
- else
- DisableVortexSphereFromVortexWeapon( vortexWeapon )
- */
-
- return totalfired
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_weapon_dialogue.nut b/Northstar.CustomServers/scripts/vscripts/weapons/_weapon_dialogue.nut
deleted file mode 100644
index 04fd24d3..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_weapon_dialogue.nut
+++ /dev/null
@@ -1,44 +0,0 @@
-untyped
-
-globalize_all_functions
-
-function Weapon_Dialogue_Init()
-{
- RegisterConversation( "CoopTD_TurretAvailable", VO_PRIORITY_PLAYERSTATE ) // player turret becomes available
- RegisterConversation( "CoopTD_TurretAvailableNag", VO_PRIORITY_PLAYERSTATE ) // player turret available nag
- RegisterConversation( "CoopTD_TurretDestroyed", VO_PRIORITY_PLAYERSTATE ) // player turret destroyed
- RegisterConversation( "CoopTD_TurretDeadAndReady", VO_PRIORITY_PLAYERSTATE ) // player turret destroyed, and one ready
- RegisterConversation( "CoopTD_TurretKillstreak", VO_PRIORITY_PLAYERSTATE ) // player turret has lots of kills
- RegisterConversation( "CoopTD_TurretKilledTitan", VO_PRIORITY_PLAYERSTATE ) // player turret killed a titan
- RegisterConversation( "CoopTD_TurretKilledTitan_Multi", VO_PRIORITY_PLAYERSTATE ) // player turret killed multiple titans
-
- #if CLIENT
-
- AddVDULineForSarah( "CoopTD_TurretAvailable", "diag_gm_coop_playerTurretEarned_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretAvailable", "diag_gm_coop_playerTurretEarned_mcor_Sarah" )
-
- // player turret available nag
- AddVDULineForSarah( "CoopTD_TurretAvailableNag", "diag_gm_coop_playerTurretNag_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretAvailableNag", "diag_gm_coop_playerTurretNag_mcor_Sarah" )
-
- // player turret destroyed
- AddVDULineForSarah( "CoopTD_TurretDestroyed", "diag_gm_coop_playerTurretDestro_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretDestroyed", "diag_gm_coop_playerTurretDestro_mcor_Sarah" )
-
- // player turret destroyed and another one ready
- AddVDULineForSarah( "CoopTD_TurretDeadAndReady", "diag_gm_coop_playerTurretDeadAndReady_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretDeadAndReady", "diag_gm_coop_playerTurretDeadAndReady_mcor_Sarah" )
-
- // player turret has lots of kills
- AddVDULineForSarah( "CoopTD_TurretKillstreak", "diag_gm_coop_playerTurretHighKills_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretKillstreak", "diag_gm_coop_playerTurretHighKills_mcor_Sarah" )
-
- // player turret killed a titan
- AddVDULineForSarah( "CoopTD_TurretKilledTitan", "diag_gm_coop_playerTurretKilledTitan_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretKilledTitan", "diag_gm_coop_playerTurretKilledTitan_mcor_Sarah" )
-
- // player turret killed multiple titans
- AddVDULineForSarah( "CoopTD_TurretKilledTitan_Multi", "diag_gm_coop_playerTurretKilledTitanAgain_mcor_Sarah" )
- AddVDULineForSpyglass( "CoopTD_TurretKilledTitan_Multi", "diag_gm_coop_playerTurretKilledTitanAgain_mcor_Sarah" )
- #endif
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/weapons/_weapon_utility.nut b/Northstar.CustomServers/scripts/vscripts/weapons/_weapon_utility.nut
deleted file mode 100644
index b3e5f5a3..00000000
--- a/Northstar.CustomServers/scripts/vscripts/weapons/_weapon_utility.nut
+++ /dev/null
@@ -1,3966 +0,0 @@
-untyped
-
-//TODO: Should split this up into server, client and shared versions and just globalize_all_functions
-global function WeaponUtility_Init
-
-global function ApplyVectorSpread
-global function DebugDrawMissilePath
-global function DegreesToTarget
-global function DetonateAllPlantedExplosives
-global function EntityCanHaveStickyEnts
-global function EntityShouldStick
-global function FireExpandContractMissiles
-global function FireExpandContractMissiles_S2S
-global function GetVectorFromPositionToCrosshair
-global function GetVelocityForDestOverTime
-global function GetPlayerVelocityForDestOverTime
-global function GetWeaponBurnMods
-global function InitMissileForRandomDriftForVortexLow
-global function IsPilotShotgunWeapon
-global function PlantStickyEntity
-global function PlantStickyEntityThatBouncesOffWalls
-global function PlantStickyEntityOnWorldThatBouncesOffWalls
-global function PlantStickyGrenade
-global function PlantSuperStickyGrenade
-global function Player_DetonateSatchels
-global function PROTO_CanPlayerDeployWeapon
-global function ProximityCharge_PostFired_Init
-global function RegenerateOffhandAmmoOverTime
-global function ShotgunBlast
-global function FireGenericBoltWithDrop
-global function OnWeaponPrimaryAttack_GenericBoltWithDrop_Player
-global function OnWeaponPrimaryAttack_GenericMissile_Player
-global function OnWeaponActivate_updateViewmodelAmmo
-global function TEMP_GetDamageFlagsFromProjectile
-global function WeaponCanCrit
-global function GiveEMPStunStatusEffects
-global function GetPrimaryWeapons
-global function GetSidearmWeapons
-global function GetATWeapons
-global function GetPlayerFromTitanWeapon
-global function ChargeBall_Precache
-global function ChargeBall_FireProjectile
-global function ChargeBall_ChargeBegin
-global function ChargeBall_ChargeEnd
-global function ChargeBall_StopChargeEffects
-global function ChargeBall_GetChargeTime
-
-global function PlayerUsedOffhand
-#if SERVER
-global function SetPlayerCooldowns
-global function ResetPlayerCooldowns
-global function StoreOffhandData
-#endif
-
-global function GetRadiusDamageDataFromProjectile
-
-#if DEV
-global function DevPrintAllStatusEffectsOnEnt
-#endif // #if DEV
-
-#if SERVER
- global function ClusterRocket_Detonate
- global function PassThroughDamage
- global function PROTO_CleanupTrackedProjectiles
- global function PROTO_InitTrackedProjectile
- global function PROTO_PlayTrapLightEffect
- global function Satchel_PostFired_Init
- global function StartClusterExplosions
- global function TrapDestroyOnRoundEnd
- global function TrapExplodeOnDamage
- global function PROTO_DelayCooldown
- global function PROTO_FlakCannonMissiles
- global function GetBulletPassThroughTargets
- global function IsValidPassThroughTarget
- global function GivePlayerAmpedWeapon
- global function GivePlayerAmpedWeaponAndSetAsActive
- global function ReplacePlayerOffhand
- global function ReplacePlayerOrdnance
- global function DisableWeapons
- global function EnableWeapons
- global function WeaponAttackWave
- global function AddActiveThermiteBurn
- global function GetActiveThermiteBurnsWithinRadius
- global function OnWeaponPrimaryAttack_GenericBoltWithDrop_NPC
- global function OnWeaponPrimaryAttack_GenericMissile_NPC
- global function EMP_DamagedPlayerOrNPC
- global function EMP_FX
- global function GetWeaponDPS
- global function GetTTK
- global function GetWeaponModsFromDamageInfo
- global function Thermite_DamagePlayerOrNPCSounds
- global function AddThreatScopeColorStatusEffect
- global function RemoveThreatScopeColorStatusEffect
-#endif //SERVER
-#if CLIENT
- global function GlobalClientEventHandler
- global function UpdateViewmodelAmmo
- global function ServerCallback_AirburstIconUpdate
- global function ServerCallback_GuidedMissileDestroyed
- global function IsOwnerViewPlayerFullyADSed
-#endif //CLIENT
-
-global const PROJECTILE_PREDICTED = true
-global const PROJECTILE_NOT_PREDICTED = false
-
-global const PROJECTILE_LAG_COMPENSATED = true
-global const PROJECTILE_NOT_LAG_COMPENSATED = false
-
-const float EMP_SEVERITY_SLOWTURN = 0.35
-const float EMP_SEVERITY_SLOWMOVE = 0.50
-const float LASER_STUN_SEVERITY_SLOWTURN = 0.20
-const float LASER_STUN_SEVERITY_SLOWMOVE = 0.30
-
-const asset FX_EMP_BODY_HUMAN = $"P_emp_body_human"
-const asset FX_EMP_BODY_TITAN = $"P_emp_body_titan"
-const asset FX_VANGUARD_ENERGY_BODY_HUMAN = $"P_monarchBeam_body_human"
-const asset FX_VANGUARD_ENERGY_BODY_TITAN = $"P_monarchBeam_body_titan"
-const SOUND_EMP_REBOOT_SPARKS = "marvin_weld"
-const FX_EMP_REBOOT_SPARKS = $"weld_spark_01_sparksfly"
-const EMP_GRENADE_BEAM_EFFECT = $"wpn_arc_cannon_beam"
-const DRONE_REBOOT_TIME = 5.0
-const GUNSHIP_REBOOT_TIME = 5.0
-
-global struct RadiusDamageData
-{
- int explosionDamage
- int explosionDamageHeavyArmor
- float explosionRadius
- float explosionInnerRadius
-}
-
-#if SERVER
-
-global struct PopcornInfo
-{
- string weaponName
- array weaponMods // could be array< string >
- int damageSourceId
- int count
- float delay
- float offset
- float range
- vector normal
- float duration
- int groupSize
- bool hasBase
-}
-
-struct ColorSwapStruct
-{
- int statusEffectId
- entity weaponOwner
-}
-
-struct
-{
- float titanRocketLauncherTitanDamageRadius
- float titanRocketLauncherOtherDamageRadius
-
- int activeThermiteBurnsManagedEnts
- array<ColorSwapStruct> colorSwapStatusEffects
-} file
-
-global int HOLO_PILOT_TRAIL_FX
-
-global struct HoverSounds
-{
- string liftoff_1p
- string liftoff_3p
- string hover_1p
- string hover_3p
- string descent_1p
- string descent_3p
- string landing_1p
- string landing_3p
-}
-
-#endif
-
-function WeaponUtility_Init()
-{
- level.weaponsPrecached <- {}
-
- // what classes can sticky thrown entities stick to?
- level.stickyClasses <- {}
- level.stickyClasses[ "worldspawn" ] <- true
- level.stickyClasses[ "player" ] <- true
- level.stickyClasses[ "prop_dynamic" ] <- true
- level.stickyClasses[ "prop_script" ] <- true
- level.stickyClasses[ "func_brush" ] <- true
- level.stickyClasses[ "func_brush_lightweight" ] <- true
- level.stickyClasses[ "phys_bone_follower" ] <- true
-
- level.trapChainReactClasses <- {}
- level.trapChainReactClasses[ "mp_weapon_frag_grenade" ] <- true
- level.trapChainReactClasses[ "mp_weapon_satchel" ] <- true
- level.trapChainReactClasses[ "mp_weapon_proximity_mine" ] <- true
- level.trapChainReactClasses[ "mp_weapon_laser_mine" ] <- true
-
- RegisterSignal( "Planted" )
- RegisterSignal( "EMP_FX" )
- RegisterSignal( "ArcStunned" )
-
- PrecacheParticleSystem( EMP_GRENADE_BEAM_EFFECT )
- PrecacheParticleSystem( FX_EMP_BODY_TITAN )
- PrecacheParticleSystem( FX_EMP_BODY_HUMAN )
- PrecacheParticleSystem( FX_VANGUARD_ENERGY_BODY_HUMAN )
- PrecacheParticleSystem( FX_VANGUARD_ENERGY_BODY_TITAN )
- PrecacheParticleSystem( FX_EMP_REBOOT_SPARKS )
-
- PrecacheImpactEffectTable( CLUSTER_ROCKET_FX_TABLE )
-
- #if SERVER
- AddDamageCallbackSourceID( eDamageSourceId.mp_titanweapon_triple_threat, TripleThreatGrenade_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_defender, Defender_DamagedPlayerOrNPC )
- //AddDamageCallbackSourceID( eDamageSourceId.mp_titanweapon_rocketeer_rocketstream, TitanRocketLauncher_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_smr, SMR_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_flak_rifle, PROTO_Flak_Rifle_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_titanweapon_stun_laser, VanguardEnergySiphon_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_grenade_emp, EMP_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId.mp_weapon_proximity_mine, EMP_DamagedPlayerOrNPC )
- AddDamageCallbackSourceID( eDamageSourceId[ CHARGE_TOOL ], EMP_DamagedPlayerOrNPC )
- if ( IsMultiplayer() )
- AddCallback_OnPlayerRespawned( PROTO_TrackedProjectile_OnPlayerRespawned )
- AddCallback_OnPlayerKilled( PAS_CooldownReduction_OnKill )
- AddCallback_OnPlayerGetsNewPilotLoadout( OnPlayerGetsNewPilotLoadout )
- AddCallback_OnPlayerKilled( OnPlayerKilled )
-
- file.activeThermiteBurnsManagedEnts = CreateScriptManagedEntArray()
-
- AddCallback_EntitiesDidLoad( EntitiesDidLoad )
-
- HOLO_PILOT_TRAIL_FX = PrecacheParticleSystem( $"P_ar_holopilot_trail" )
-
- PrecacheParticleSystem( $"wpn_laser_blink" )
- PrecacheParticleSystem( $"wpn_laser_blink_fast" )
- PrecacheParticleSystem( $"P_ordinance_icon_owner" )
- #endif
-}
-
-#if SERVER
-void function EntitiesDidLoad()
-{
-#if SP
- // if we are going to do this, it should happen in the weapon, not globally
- //float titanRocketLauncherInnerRadius = expect float( GetWeaponInfoFileKeyField_Global( "mp_titanweapon_rocketeer_rocketstream", "explosion_inner_radius" ) )
- //float titanRocketLauncherOuterRadius = expect float( GetWeaponInfoFileKeyField_Global( "mp_titanweapon_rocketeer_rocketstream", "explosionradius" ) )
- //file.titanRocketLauncherTitanDamageRadius = titanRocketLauncherInnerRadius + ( ( titanRocketLauncherOuterRadius - titanRocketLauncherInnerRadius ) * 0.4 )
- //file.titanRocketLauncherOtherDamageRadius = titanRocketLauncherInnerRadius + ( ( titanRocketLauncherOuterRadius - titanRocketLauncherInnerRadius ) * 0.1 )
-#endif
-}
-#endif
-
-////////////////////////////////////////////////////////////////////
-
-#if CLIENT
-void function GlobalClientEventHandler( entity weapon, string name )
-{
- if ( name == "ammo_update" )
- UpdateViewmodelAmmo( false, weapon )
-
- if ( name == "ammo_full" )
- UpdateViewmodelAmmo( true, weapon )
-}
-
-function UpdateViewmodelAmmo( bool forceFull, entity weapon )
-{
- Assert( weapon != null ) // used to be: if ( weapon == null ) weapon = this.self
-
- if ( !IsValid( weapon ) )
- return
- if ( !IsLocalViewPlayer( weapon.GetWeaponOwner() ) )
- return
-
- int bodyGroupCount = weapon.GetWeaponSettingInt( eWeaponVar.bodygroup_ammo_index_count )
- if ( bodyGroupCount <= 0 )
- return
-
- int rounds = weapon.GetWeaponPrimaryClipCount()
- int maxRoundsForClipSize = weapon.GetWeaponPrimaryClipCountMax()
- int maxRoundsForBodyGroup = (bodyGroupCount - 1)
- int maxRounds = minint( maxRoundsForClipSize, maxRoundsForBodyGroup )
-
- if ( forceFull || (rounds > maxRounds) )
- rounds = maxRounds
-
- //printt( "ROUNDS:", rounds, "/", maxRounds )
- weapon.SetViewmodelAmmoModelIndex( rounds )
-}
-#endif // #if CLIENT
-
-void function OnWeaponActivate_updateViewmodelAmmo( entity weapon )
-{
-#if CLIENT
- UpdateViewmodelAmmo( false, weapon )
-#endif // #if CLIENT
-}
-
-#if SERVER
-//////////////////WEAPON DAMAGE CALLBACKS/////////////////////////////////////////
-void function TripleThreatGrenade_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- if ( !IsValid( ent ) )
- return
-
- if ( ent.GetClassName() == "grenade_frag" )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- vector damagePosition = DamageInfo_GetDamagePosition( damageInfo )
-
- vector entOrigin = ent.GetOrigin()
- vector entCenter = ent.GetWorldSpaceCenter()
- float distanceToOrigin = Distance( entOrigin, damagePosition )
- float distanceToCenter = Distance( entCenter, damagePosition )
-
- vector normal = Vector( 0, 0, 1 )
- entity inflictor = DamageInfo_GetInflictor( damageInfo )
- if ( IsValid( inflictor.s ) )
- {
- if ( "collisionNormal" in inflictor.s )
- normal = expect vector( inflictor.s.collisionNormal )
- }
-
- local zDifferenceOrigin = deg_cos( DegreesToTarget( entOrigin, normal, damagePosition ) ) * distanceToOrigin
- local zDifferenceTop = deg_cos( DegreesToTarget( entCenter, normal, damagePosition ) ) * distanceToCenter - (entCenter.z - entOrigin.z)
-
- float zDamageDiff
- //Full damage if explosion is between Origin or Center.
- if ( zDifferenceOrigin > 0 && zDifferenceTop < 0 )
- zDamageDiff = 1.0
- else if ( zDifferenceTop > 0 )
- zDamageDiff = GraphCapped( zDifferenceTop, 0.0, 32.0, 1.0, 0.0 )
- else
- zDamageDiff = GraphCapped( zDifferenceOrigin, 0.0, -32.0, 1.0, 0.0 )
-
- DamageInfo_ScaleDamage( damageInfo, zDamageDiff )
-}
-
-void function Defender_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- if ( !IsValid( ent ) )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- local damage = Vortex_HandleElectricDamage( ent, DamageInfo_GetAttacker( damageInfo ), DamageInfo_GetDamage( damageInfo ), DamageInfo_GetWeapon( damageInfo ) )
- DamageInfo_SetDamage( damageInfo, damage )
-}
-
-/*
-void function TitanRocketLauncher_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- Assert( IsSingleplayer() )
-
- if ( !IsValid( ent ) )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- vector damagePosition = DamageInfo_GetDamagePosition( damageInfo )
-
- if ( ent == DamageInfo_GetAttacker( damageInfo ) )
- return
-
- if ( ent.IsTitan() )
- {
- vector entOrigin = ent.GetOrigin()
- if ( Distance( damagePosition, entOrigin ) > file.titanRocketLauncherTitanDamageRadius )
- DamageInfo_SetDamage( damageInfo, 0 )
- }
- else if ( IsHumanSized( ent ) )
- {
- if ( Distance( damagePosition, ent.GetOrigin() ) > file.titanRocketLauncherOtherDamageRadius )
- DamageInfo_SetDamage( damageInfo, 0 )
- }
-}
-*/
-
-void function SMR_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- //Hack - JFS ( The explosion radius is too small on the SMR to deal splash damage to pilots on a Titan. )
- if ( !IsValid( ent ) )
- return
-
- if ( !ent.IsTitan() )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- entity attacker = DamageInfo_GetAttacker( damageInfo )
-
- if ( IsValid( attacker ) && attacker.IsPlayer() && attacker.GetTitanSoulBeingRodeoed() == ent.GetTitanSoul() )
- attacker.TakeDamage( 30, attacker, attacker, { scriptType = DF_GIB | DF_EXPLOSION, damageSourceId = eDamageSourceId.mp_weapon_smr, weapon = DamageInfo_GetWeapon( damageInfo ) } )
-}
-
-
-void function PROTO_Flak_Rifle_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- entity attacker = DamageInfo_GetAttacker( damageInfo )
-
- if ( !IsValid( ent ) || !IsValid( attacker ) )
- return
-
- if ( attacker == ent )
- DamageInfo_ScaleDamage( damageInfo, 0.5 )
-}
-
-function EngineerRocket_DamagedPlayerOrNPC( ent, damageInfo )
-{
- expect entity( ent )
-
- entity attacker = DamageInfo_GetAttacker( damageInfo )
-
- if ( !IsValid( ent ) || !IsValid( attacker ) )
- return
-
- if ( attacker == ent )
- DamageInfo_SetDamage( damageInfo, 10 )
-}
-///////////////////////////////////////////////////////////////////////
-
-#endif // SERVER
-
-vector function ApplyVectorSpread( vector vecShotDirection, float spreadDegrees, float bias = 1.0 )
-{
- vector angles = VectorToAngles( vecShotDirection )
- vector vecUp = AnglesToUp( angles )
- vector vecRight = AnglesToRight( angles )
-
- float sinDeg = deg_sin( spreadDegrees / 2.0 )
-
- // get circular gaussian spread
- float x
- float y
- float z
-
- if ( bias > 1.0 )
- bias = 1.0
- else if ( bias < 0.0 )
- bias = 0.0
-
- // code gets these values from cvars ai_shot_bias_min & ai_shot_bias_max
- float shotBiasMin = -1.0
- float shotBiasMax = 1.0
-
- // 1.0 gaussian, 0.0 is flat, -1.0 is inverse gaussian
- float shotBias = ( ( shotBiasMax - shotBiasMin ) * bias ) + shotBiasMin
- float flatness = ( fabs(shotBias) * 0.5 )
-
- while ( true )
- {
- x = RandomFloatRange( -1.0, 1.0 ) * flatness + RandomFloatRange( -1.0, 1.0 ) * (1 - flatness)
- y = RandomFloatRange( -1.0, 1.0 ) * flatness + RandomFloatRange( -1.0, 1.0 ) * (1 - flatness)
- if ( shotBias < 0 )
- {
- x = ( x >= 0 ) ? 1.0 - x : -1.0 - x
- y = ( y >= 0 ) ? 1.0 - y : -1.0 - y
- }
- z = x * x + y * y
-
- if ( z <= 1 )
- break
- }
-
- vector addX = vecRight * ( x * sinDeg )
- vector addY = vecUp * ( y * sinDeg )
- vector m_vecResult = vecShotDirection + addX + addY
-
- return m_vecResult
-}
-
-
-float function DegreesToTarget( vector origin, vector forward, vector targetPos )
-{
- vector dirToTarget = targetPos - origin
- dirToTarget = Normalize( dirToTarget )
- float dot = DotProduct( forward, dirToTarget )
- float degToTarget = (acos( dot ) * 180 / PI)
-
- return degToTarget
-}
-
-function ShotgunBlast( entity weapon, vector pos, vector dir, int numBlasts, int damageType, float damageScaler = 1.0, float ornull maxAngle = null, float ornull maxDistance = null )
-{
- Assert( numBlasts > 0 )
- int numBlastsOriginal = numBlasts
- entity owner = weapon.GetWeaponOwner()
-
- /*
- Debug ConVars:
- visible_ent_cone_debug_duration_client - Set to non-zero to see debug output
- visible_ent_cone_debug_duration_server - Set to non-zero to see debug output
- visible_ent_cone_debug_draw_radius - Size of trace endpoint debug draw
- */
-
- if ( maxDistance == null )
- maxDistance = weapon.GetMaxDamageFarDist()
- expect float( maxDistance )
-
- if ( maxAngle == null )
- maxAngle = owner.GetAttackSpreadAngle() * 0.5
- expect float( maxAngle )
-
- array<entity> ignoredEntities = [ owner ]
- int traceMask = TRACE_MASK_SHOT
- int visConeFlags = VIS_CONE_ENTS_TEST_HITBOXES | VIS_CONE_ENTS_CHECK_SOLID_BODY_HIT | VIS_CONE_ENTS_APPOX_CLOSEST_HITBOX | VIS_CONE_RETURN_HIT_VORTEX
-
- entity antilagPlayer
- if ( owner.IsPlayer() )
- {
- if ( owner.IsPhaseShifted() )
- return;
-
- antilagPlayer = owner
- }
-
- //JFS - Bug 198500
- Assert( maxAngle > 0.0, "JFS returning out at this instance. We need to investigate when a valid mp_titanweapon_laser_lite weapon returns 0 spread")
- if ( maxAngle == 0.0 )
- return
-
- array<VisibleEntityInCone> results = FindVisibleEntitiesInCone( pos, dir, maxDistance, (maxAngle * 1.1), ignoredEntities, traceMask, visConeFlags, antilagPlayer, weapon )
- foreach ( result in results )
- {
- float angleToHitbox = 0.0
- if ( !result.solidBodyHit )
- angleToHitbox = DegreesToTarget( pos, dir, result.approxClosestHitboxPos )
-
- numBlasts -= ShotgunBlastDamageEntity( weapon, pos, dir, result, angleToHitbox, maxAngle, numBlasts, damageType, damageScaler )
- if ( numBlasts <= 0 )
- break
- }
-
- //Something in the TakeDamage above is triggering the weapon owner to become invalid.
- owner = weapon.GetWeaponOwner()
- if ( !IsValid( owner ) )
- return
-
- // maxTracer limit set in /r1dev/src/game/client/c_player.h
- const int MAX_TRACERS = 16
- bool didHitAnything = ((numBlastsOriginal - numBlasts) != 0)
- bool doTraceBrushOnly = (!didHitAnything)
- if ( numBlasts > 0 )
- weapon.FireWeaponBullet_Special( pos, dir, minint( numBlasts, MAX_TRACERS ), damageType, false, false, true, false, false, false, doTraceBrushOnly )
-}
-
-
-const SHOTGUN_ANGLE_MIN_FRACTION = 0.1;
-const SHOTGUN_ANGLE_MAX_FRACTION = 1.0;
-const SHOTGUN_DAMAGE_SCALE_AT_MIN_ANGLE = 0.8;
-const SHOTGUN_DAMAGE_SCALE_AT_MAX_ANGLE = 0.1;
-
-int function ShotgunBlastDamageEntity( entity weapon, vector barrelPos, vector barrelVec, VisibleEntityInCone result, float angle, float maxAngle, int numPellets, int damageType, float damageScaler )
-{
- entity target = result.ent
-
- //The damage scaler is currently only > 1 for the Titan Shotgun alt fire.
- if ( !target.IsTitan() && damageScaler > 1 )
- damageScaler = max( damageScaler * 0.4, 1.5 )
-
- entity owner = weapon.GetWeaponOwner()
- // Ent in cone not valid
- if ( !IsValid( target ) || !IsValid( owner ) )
- return 0
-
- // Fire fake bullet towards entity for visual purposes only
- vector hitLocation = result.visiblePosition
- vector vecToEnt = ( hitLocation - barrelPos )
- vecToEnt.Norm()
- if ( Length( vecToEnt ) == 0 )
- vecToEnt = barrelVec
-
- // This fires a fake bullet that doesn't do any damage. Currently it triggeres a damage callback with 0 damage which is bad.
- weapon.FireWeaponBullet_Special( barrelPos, vecToEnt, 1, damageType, true, true, true, false, false, false, false ) // fires perfect bullet with no antilag and no spread
-
-#if SERVER
- // Determine how much damage to do based on distance
- float distanceToTarget = Distance( barrelPos, hitLocation )
-
- if ( !result.solidBodyHit ) // non solid hits take 1 blast more
- distanceToTarget += 130
-
- int extraMods = result.extraMods
- float damageAmount = CalcWeaponDamage( owner, target, weapon, distanceToTarget, extraMods )
-
- // vortex needs to scale damage based on number of rounds absorbed
- string className = weapon.GetWeaponClassName()
- if ( (className == "mp_titanweapon_vortex_shield") || (className == "mp_titanweapon_vortex_shield_ion") || (className == "mp_titanweapon_heat_shield") )
- {
- damageAmount *= numPellets
- //printt( "scaling vortex hitscan output damage by", numPellets, "pellets for", weaponNearDamageTitan, "damage vs titans" )
- }
-
- float coneScaler = 1.0
- //if ( angle > 0 )
- // coneScaler = GraphCapped( angle, (maxAngle * SHOTGUN_ANGLE_MIN_FRACTION), (maxAngle * SHOTGUN_ANGLE_MAX_FRACTION), SHOTGUN_DAMAGE_SCALE_AT_MIN_ANGLE, SHOTGUN_DAMAGE_SCALE_AT_MAX_ANGLE )
-
- // Calculate the final damage abount to inflict on the target. Also scale it by damageScaler which may have been passed in by script ( used by alt fire mode on titan shotgun to fire multiple shells )
- float finalDamageAmount = damageAmount * coneScaler * damageScaler
- //printt( "angle:", angle, "- coneScaler:", coneScaler, "- damageAmount:", damageAmount, "- damageScaler:", damageScaler, " = finalDamageAmount:", finalDamageAmount )
-
- // Calculate impulse force to apply based on damage
- int maxImpulseForce = expect int( weapon.GetWeaponInfoFileKeyField( "impulse_force" ) )
- float impulseForce = float( maxImpulseForce ) * coneScaler * damageScaler
- vector impulseVec = barrelVec * impulseForce
-
- int damageSourceID = weapon.GetDamageSourceID()
-
- //
- float critScale = weapon.GetWeaponSettingFloat( eWeaponVar.critical_hit_damage_scale )
- target.TakeDamage( finalDamageAmount, owner, weapon, { origin = hitLocation, force = impulseVec, scriptType = damageType, damageSourceId = damageSourceID, weapon = weapon, hitbox = result.visibleHitbox, criticalHitScale = critScale } )
-
- //printt( "-----------" )
- //printt( " distanceToTarget:", distanceToTarget )
- //printt( " damageAmount:", damageAmount )
- //printt( " coneScaler:", coneScaler )
- //printt( " impulseForce:", impulseForce )
- //printt( " impulseVec:", impulseVec.x + ", " + impulseVec.y + ", " + impulseVec.z )
- //printt( " finalDamageAmount:", finalDamageAmount )
- //PrintTable( result )
-#endif // #if SERVER
-
- return 1
-}
-
-int function FireGenericBoltWithDrop( entity weapon, WeaponPrimaryAttackParams attackParams, bool isPlayerFired )
-{
-#if CLIENT
- if ( !weapon.ShouldPredictProjectiles() )
- return 1
-#endif // #if CLIENT
-
- weapon.EmitWeaponNpcSound( LOUD_WEAPON_AI_SOUND_RADIUS_MP, 0.2 )
-
- const float PROJ_SPEED_SCALE = 1
- const float PROJ_GRAVITY = 1
- int damageFlags = weapon.GetWeaponDamageFlags()
- entity bolt = weapon.FireWeaponBolt( attackParams.pos, attackParams.dir, PROJ_SPEED_SCALE, damageFlags, damageFlags, isPlayerFired, 0 )
- if ( bolt != null )
- {
- bolt.kv.gravity = PROJ_GRAVITY
- bolt.kv.rendercolor = "0 0 0"
- bolt.kv.renderamt = 0
- bolt.kv.fadedist = 1
- }
-
- return 1
-}
-var function OnWeaponPrimaryAttack_GenericBoltWithDrop_Player( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- return FireGenericBoltWithDrop( weapon, attackParams, true )
-}
-
-var function OnWeaponPrimaryAttack_EPG( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- entity missile = weapon.FireWeaponMissile( attackParams.pos, attackParams.dir, 1, damageTypes.largeCaliberExp, damageTypes.largeCaliberExp, false, PROJECTILE_NOT_PREDICTED )
- if ( missile )
- {
- EmitSoundOnEntity( missile, "Weapon_Sidwinder_Projectile" )
- missile.InitMissileForRandomDriftFromWeaponSettings( attackParams.pos, attackParams.dir )
- }
-
- return missile
-}
-
-#if SERVER
-var function OnWeaponPrimaryAttack_GenericBoltWithDrop_NPC( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- return FireGenericBoltWithDrop( weapon, attackParams, false )
-}
-#endif // #if SERVER
-
-
-var function OnWeaponPrimaryAttack_GenericMissile_Player( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- weapon.EmitWeaponNpcSound( LOUD_WEAPON_AI_SOUND_RADIUS_MP, 0.2 )
-
- entity weaponOwner = weapon.GetWeaponOwner()
- vector bulletVec = ApplyVectorSpread( attackParams.dir, weaponOwner.GetAttackSpreadAngle() - 1.0 )
- attackParams.dir = bulletVec
-
- if ( IsServer() || weapon.ShouldPredictProjectiles() )
- {
- entity missile = weapon.FireWeaponMissile( attackParams.pos, attackParams.dir, 1.0, weapon.GetWeaponDamageFlags(), weapon.GetWeaponDamageFlags(), false, PROJECTILE_PREDICTED )
- if ( missile )
- {
- missile.InitMissileForRandomDriftFromWeaponSettings( attackParams.pos, attackParams.dir )
- }
- }
-}
-
-#if SERVER
-var function OnWeaponPrimaryAttack_GenericMissile_NPC( entity weapon, WeaponPrimaryAttackParams attackParams )
-{
- weapon.EmitWeaponNpcSound( LOUD_WEAPON_AI_SOUND_RADIUS_MP, 0.2 )
-
- entity missile = weapon.FireWeaponMissile( attackParams.pos, attackParams.dir, 1.0, weapon.GetWeaponDamageFlags(), weapon.GetWeaponDamageFlags(), true, PROJECTILE_NOT_PREDICTED )
- if ( missile )
- {
- missile.InitMissileForRandomDriftFromWeaponSettings( attackParams.pos, attackParams.dir )
- }
-}
-#endif // #if SERVER
-
-bool function PlantStickyEntityOnWorldThatBouncesOffWalls( entity ent, table collisionParams, float bounceDot )
-{
- entity hitEnt = expect entity( collisionParams.hitEnt )
- if ( hitEnt && ( hitEnt.IsWorld() || hitEnt.HasPusherRootParent() ) )
- {
- float dot = expect vector( collisionParams.normal ).Dot( Vector( 0, 0, 1 ) )
-
- if ( dot < bounceDot )
- return false
-
- return PlantStickyEntity( ent, collisionParams )
- }
-
- return false
-}
-
-bool function PlantStickyEntityThatBouncesOffWalls( entity ent, table collisionParams, float bounceDot )
-{
- if ( expect entity( collisionParams.hitEnt ) == GetEntByIndex( 0 ) )
- {
- // Satchel hit the world
- float dot = expect vector( collisionParams.normal ).Dot( Vector( 0, 0, 1 ) )
-
- if ( dot < bounceDot )
- return false
- }
-
- return PlantStickyEntity( ent, collisionParams )
-}
-
-
-bool function PlantStickyEntity( entity ent, table collisionParams, vector angleOffset = <0.0, 0.0, 0.0> )
-{
- if ( !EntityShouldStick( ent, expect entity( collisionParams.hitEnt ) ) )
- return false
-
- // Don't allow parenting to another "sticky" entity to prevent them parenting onto each other
- if ( collisionParams.hitEnt.IsProjectile() )
- return false
-
- // Update normal from last bouce so when it explodes it can orient the effect properly
-
- vector plantAngles = AnglesCompose( VectorToAngles( collisionParams.normal ), angleOffset )
- vector plantPosition = expect vector( collisionParams.pos )
-
- if ( !LegalOrigin( plantPosition ) )
- return false
-
- #if SERVER
- ent.SetAbsOrigin( plantPosition )
- ent.SetAbsAngles( plantAngles )
- ent.proj.isPlanted = true
- #else
- ent.SetOrigin( plantPosition )
- ent.SetAngles( plantAngles )
- #endif
- ent.SetVelocity( Vector( 0, 0, 0 ) )
-
- //printt( " - Hitbox is:", collisionParams.hitbox, " IsWorld:", collisionParams.hitEnt )
- if ( !collisionParams.hitEnt.IsWorld() )
- {
- if ( !ent.IsMarkedForDeletion() && !collisionParams.hitEnt.IsMarkedForDeletion() )
- {
- if ( collisionParams.hitbox > 0 )
- ent.SetParentWithHitbox( collisionParams.hitEnt, collisionParams.hitbox, true )
-
- // Hit a func_brush
- else
- ent.SetParent( collisionParams.hitEnt )
-
- if ( collisionParams.hitEnt.IsPlayer() )
- {
- thread HandleDisappearingParent( ent, expect entity( collisionParams.hitEnt ) )
- }
- }
- }
- else
- {
- ent.SetVelocity( Vector( 0, 0, 0 ) )
- ent.StopPhysics()
- }
- #if CLIENT
- if ( ent instanceof C_BaseGrenade )
- #else
- if ( ent instanceof CBaseGrenade )
- #endif
- ent.MarkAsAttached()
-
- ent.Signal( "Planted" )
-
- return true
-}
-
-bool function PlantStickyGrenade( entity ent, vector pos, vector normal, entity hitEnt, int hitbox, float depth = 0.0, bool allowBounce = true, bool allowEntityStick = true )
-{
- if ( ent.GetTeam() == hitEnt.GetTeam() )
- return false
-
- if ( ent.IsMarkedForDeletion() || hitEnt.IsMarkedForDeletion() )
- return false
-
- vector plantAngles = VectorToAngles( normal )
- vector plantPosition = pos + normal * -depth
-
- if ( !allowBounce )
- ent.SetVelocity( Vector( 0, 0, 0 ) )
-
- if ( !LegalOrigin( plantPosition ) )
- return false
-
- #if SERVER
- ent.SetAbsOrigin( plantPosition )
- ent.SetAbsAngles( plantAngles )
- ent.proj.isPlanted = true
- #else
- ent.SetOrigin( plantPosition )
- ent.SetAngles( plantAngles )
- #endif
-
- if ( !hitEnt.IsWorld() && (!hitEnt.IsTitan() || !allowEntityStick) )
- return false
-
- // SetOrigin might be causing the ent to get markedForDeletion.
- if ( ent.IsMarkedForDeletion() )
- return false
-
- ent.SetVelocity( Vector( 0, 0, 0 ) )
-
- if ( hitEnt.IsWorld() )
- {
- ent.SetParent( hitEnt, "", true )
- ent.StopPhysics()
- }
- else
- {
- if ( hitbox > 0 )
- ent.SetParentWithHitbox( hitEnt, hitbox, true )
- else // Hit a func_brush
- ent.SetParent( hitEnt )
-
- if ( hitEnt.IsPlayer() )
- {
- thread HandleDisappearingParent( ent, hitEnt )
- }
- }
-
- #if CLIENT
- if ( ent instanceof C_BaseGrenade )
- ent.MarkAsAttached()
- #else
- if ( ent instanceof CBaseGrenade )
- ent.MarkAsAttached()
- #endif
-
- return true
-}
-
-
-bool function PlantSuperStickyGrenade( entity ent, vector pos, vector normal, entity hitEnt, int hitbox )
-{
- if ( ent.GetTeam() == hitEnt.GetTeam() )
- return false
-
- vector plantAngles = VectorToAngles( normal )
- vector plantPosition = pos
-
- if ( !LegalOrigin( plantPosition ) )
- return false
-
- #if SERVER
- ent.SetAbsOrigin( plantPosition )
- ent.SetAbsAngles( plantAngles )
- ent.proj.isPlanted = true
- #else
- ent.SetOrigin( plantPosition )
- ent.SetAngles( plantAngles )
- #endif
-
- if ( !hitEnt.IsWorld() && !hitEnt.IsPlayer() && !hitEnt.IsNPC() )
- return false
-
- ent.SetVelocity( Vector( 0, 0, 0 ) )
-
- if ( hitEnt.IsWorld() )
- {
- ent.StopPhysics()
- }
- else
- {
- if ( !ent.IsMarkedForDeletion() && !hitEnt.IsMarkedForDeletion() )
- {
- if ( hitbox > 0 )
- ent.SetParentWithHitbox( hitEnt, hitbox, true )
- else // Hit a func_brush
- ent.SetParent( hitEnt )
-
- if ( hitEnt.IsPlayer() )
- {
- thread HandleDisappearingParent( ent, hitEnt )
- }
- }
- }
-
- #if CLIENT
- if ( ent instanceof C_BaseGrenade )
- ent.MarkAsAttached()
- #else
- if ( ent instanceof CBaseGrenade )
- ent.MarkAsAttached()
- #endif
-
- return true
-}
-
-#if SERVER
-void function HandleDisappearingParent( entity ent, entity parentEnt )
-{
- parentEnt.EndSignal( "OnDeath" )
- ent.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( ent )
- {
- ent.ClearParent()
- }
- )
-
- parentEnt.WaitSignal( "StartPhaseShift" )
-}
-#else
-void function HandleDisappearingParent( entity ent, entity parentEnt )
-{
- parentEnt.EndSignal( "OnDeath" )
- ent.EndSignal( "OnDestroy" )
-
- parentEnt.WaitSignal( "StartPhaseShift" )
-
- ent.ClearParent()
-}
-#endif
-
-bool function EntityShouldStick( entity stickyEnt, entity hitent )
-{
- if ( !EntityCanHaveStickyEnts( stickyEnt, hitent ) )
- return false
-
- if ( hitent == stickyEnt )
- return false
-
- return true
-}
-
-bool function EntityCanHaveStickyEnts( entity stickyEnt, entity ent )
-{
- if ( !IsValid( ent ) )
- return false
-
- if ( ent.GetModelName() == $"" ) // valid case, other projectiles bullets, etc.. sometimes have no model
- return false;
-
- local entClassname
- if ( IsServer() )
- entClassname = ent.GetClassName()
- else
- entClassname = ent.GetSignifierName() // Can return null
-
- if ( !( entClassname in level.stickyClasses ) && !ent.IsNPC() )
- return false
-
- #if CLIENT
- if ( stickyEnt instanceof C_Projectile )
- #else
- if ( stickyEnt instanceof CProjectile )
- #endif
- {
- string weaponClassName = stickyEnt.ProjectileGetWeaponClassName()
- local stickPlayer = GetWeaponInfoFileKeyField_Global( weaponClassName, "stick_pilot" )
- local stickTitan = GetWeaponInfoFileKeyField_Global( weaponClassName, "stick_titan" )
- local stickNPC = GetWeaponInfoFileKeyField_Global( weaponClassName, "stick_npc" )
-
- if ( ent.IsTitan() && stickTitan )
- return true
- else if ( ent.IsPlayer() && stickPlayer )
- return true
- else if ( ent.IsNPC() && stickNPC )
- return true
-
- // not pilots
- if ( ent.IsPlayer() && !ent.IsTitan() )
- return false
- }
-
- return true
-}
-
-#if SERVER
-// shared with the vortex script which also needs to create satchels
-function Satchel_PostFired_Init( entity satchel, entity player )
-{
- satchel.proj.onlyAllowSmartPistolDamage = false
- thread SatchelThink( satchel, player )
-}
-
-function SatchelThink( entity satchel, entity player )
-{
- player.EndSignal("OnDestroy")
- satchel.EndSignal("OnDestroy")
-
- int satchelHealth = 15
- thread TrapExplodeOnDamage( satchel, satchelHealth )
-
- #if DEV
- // temp HACK for FX to use to figure out the size of the particle to play
- if ( Flag( "ShowExplosionRadius" ) )
- thread ShowExplosionRadiusOnExplode( satchel )
- #endif
-
- player.EndSignal( "OnDeath" )
-
- OnThreadEnd(
- function() : ( satchel )
- {
- if ( IsValid( satchel ) )
- {
- satchel.Destroy()
- }
- }
- )
-
- WaitForever()
-}
-
-#endif // SERVER
-
-function ProximityCharge_PostFired_Init( entity proximityMine, entity player )
-{
- #if SERVER
- proximityMine.proj.onlyAllowSmartPistolDamage = false
- #endif
-}
-
-function DetonateAllPlantedExplosives( entity player )
-{
- // ToDo: Could use Player_DetonateSatchels but it only tracks satchels, not laser mines.
-
- // Detonate all explosives - satchels and laser mines are also frag grenades in disguise
- array<entity> grenades = GetProjectileArrayEx( "grenade_frag", TEAM_ANY, TEAM_ANY, Vector( 0, 0, 0 ), -1 )
- foreach( grenade in grenades )
- {
- if ( grenade.GetOwner() != player )
- continue
-
- if ( grenade.ProjectileGetDamageSourceID() != eDamageSourceId.mp_weapon_satchel && grenade.ProjectileGetDamageSourceID() != eDamageSourceId.mp_weapon_proximity_mine )
- continue
-
- thread ExplodePlantedGrenadeAfterDelay( grenade, RandomFloatRange( 0.75, 0.95 ) )
- }
-}
-
-function ExplodePlantedGrenadeAfterDelay( entity grenade, float delay )
-{
- grenade.EndSignal( "OnDeath" )
- grenade.EndSignal( "OnDestroy" )
-
- float endTime = Time() + delay
-
- while ( Time() < endTime )
- {
- EmitSoundOnEntity( grenade, DEFAULT_WARNING_SFX )
- wait 0.1
- }
-
- grenade.GrenadeExplode( grenade.GetForwardVector() )
-}
-
-function Player_DetonateSatchels( entity player )
-{
- #if SERVER
- Assert( IsServer() )
-
- array<entity> traps = GetScriptManagedEntArray( player.s.activeTrapArrayId )
- traps.sort( CompareCreationReverse )
- foreach ( index, satchel in traps )
- {
- if ( IsValidSatchel( satchel ) )
- {
-
- thread PROTO_ExplodeAfterDelay( satchel, index * 0.25 )
- }
- }
- #endif
-}
-
-function IsValidSatchel( entity satchel )
-{
- #if SERVER
- if ( satchel.ProjectileGetWeaponClassName() != "mp_weapon_satchel" )
- return false
-
- if ( satchel.e.isDisabled == true )
- return false
-
- return true
- #endif
-}
-
-#if SERVER
-function PROTO_ExplodeAfterDelay( entity satchel, float delay )
-{
- satchel.EndSignal( "OnDestroy" )
-
- #if MP
- while ( !satchel.proj.isPlanted )
- {
- WaitFrame()
- }
- #endif
-
- wait delay
-
- satchel.GrenadeExplode( satchel.GetForwardVector() )
-}
-#endif
-
-
-#if DEV
-function ShowExplosionRadiusOnExplode( entity ent )
-{
- ent.WaitSignal( "OnDestroy" )
-
- float innerRadius = expect float( ent.GetWeaponInfoFileKeyField( "explosion_inner_radius" ) )
- float outerRadius = expect float( ent.GetWeaponInfoFileKeyField( "explosionradius" ) )
-
- vector org = ent.GetOrigin()
- vector angles = Vector( 0, 0, 0 )
- thread DebugDrawCircle( org, angles, innerRadius, 255, 255, 51, true, 3.0 )
- thread DebugDrawCircle( org, angles, outerRadius, 255, 255, 255, true, 3.0 )
-}
-#endif // DEV
-
-#if SERVER
-// shared between nades, satchels and laser mines
-void function TrapExplodeOnDamage( entity trapEnt, int trapEntHealth = 50, float waitMin = 0.0, float waitMax = 0.0 )
-{
- Assert( IsValid( trapEnt ), "Given trapEnt entity is not valid, fired from: " + trapEnt.ProjectileGetWeaponClassName() )
- EndSignal( trapEnt, "OnDestroy" )
-
- trapEnt.SetDamageNotifications( true )
- var results //Really should be a struct
- entity attacker
- entity inflictor
-
- while ( true )
- {
- if ( !IsValid( trapEnt ) )
- return
-
- results = WaitSignal( trapEnt, "OnDamaged" )
- attacker = expect entity( results.activator )
- inflictor = expect entity( results.inflictor )
-
- if ( IsValid( inflictor ) && inflictor == trapEnt )
- continue
-
- bool shouldDamageTrap = false
- if ( IsValid( attacker ) )
- {
- if ( trapEnt.proj.onlyAllowSmartPistolDamage )
- {
- if ( attacker.IsNPC() || attacker.IsPlayer() )
- {
- entity attackerWeapon = attacker.GetActiveWeapon()
- if ( IsValid( attackerWeapon ) && WeaponIsSmartPistolVariant( attackerWeapon ) )
- shouldDamageTrap = true
- }
- }
- else
- {
- if ( trapEnt.GetTeam() == attacker.GetTeam() )
- {
- if ( trapEnt.GetOwner() != attacker )
- shouldDamageTrap = false
- else
- shouldDamageTrap = !ProjectileIgnoresOwnerDamage( trapEnt )
- }
- else
- {
- shouldDamageTrap = true
- }
- }
- }
-
- if ( shouldDamageTrap )
- trapEntHealth -= int ( results.value ) //TODO: This returns float even though it feels like it should return int
-
- if ( trapEntHealth <= 0 )
- break
- }
-
- if ( !IsValid( trapEnt ) )
- return
-
- inflictor = expect entity( results.inflictor ) // waiting on code feature to pass inflictor with OnDamaged signal results table
-
- if ( waitMin >= 0 && waitMax > 0 )
- {
- float waitTime = RandomFloatRange( waitMin, waitMax )
-
- if ( waitTime > 0 )
- wait waitTime
- }
- else if ( IsValid( inflictor ) && (inflictor.IsProjectile() || (inflictor instanceof CWeaponX)) )
- {
- int dmgSourceID
- if ( inflictor.IsProjectile() )
- dmgSourceID = inflictor.ProjectileGetDamageSourceID()
- else
- dmgSourceID = inflictor.GetDamageSourceID()
-
- string inflictorClass = GetObitFromDamageSourceID( dmgSourceID )
-
- if ( inflictorClass in level.trapChainReactClasses )
- {
- // chain reaction delay
- Wait( RandomFloatRange( 0.2, 0.275 ) )
- }
- }
-
- if ( !IsValid( trapEnt ) )
- return
-
- if ( IsValid( attacker ) )
- {
- if ( attacker.IsPlayer() )
- {
- AddPlayerScoreForTrapDestruction( attacker, trapEnt )
- trapEnt.SetOwner( attacker )
- }
- else
- {
- entity lastAttacker = GetLastAttacker( attacker )
- if ( IsValid( lastAttacker ) )
- {
- // for chain explosions, figure out the attacking player that started the chain
- trapEnt.SetOwner( lastAttacker )
- }
- }
- }
-
- trapEnt.GrenadeExplode( trapEnt.GetForwardVector() )
-}
-
-bool function ProjectileIgnoresOwnerDamage( entity projectile )
-{
- var ignoreOwnerDamage = projectile.ProjectileGetWeaponInfoFileKeyField( "projectile_ignore_owner_damage" )
-
- if ( ignoreOwnerDamage == null )
- return false
-
- return ignoreOwnerDamage == 1
-}
-
-bool function WeaponIsSmartPistolVariant( entity weapon )
-{
- var isSP = weapon.GetWeaponInfoFileKeyField( "is_smart_pistol" )
-
- //printt( isSP )
-
- if ( isSP == null )
- return false
-
- return ( isSP == 1 )
-}
-
-// NOTE: we should stop using this
-function TrapDestroyOnRoundEnd( entity player, entity trapEnt )
-{
- trapEnt.EndSignal( "OnDestroy" )
-
- svGlobal.levelEnt.WaitSignal( "ClearedPlayers" )
-
- if ( IsValid( trapEnt ) )
- trapEnt.Destroy()
-}
-
-function AddPlayerScoreForTrapDestruction( entity player, entity trapEnt )
-{
- // don't get score for killing your own trap
- if ( "originalOwner" in trapEnt.s && trapEnt.s.originalOwner == player )
- return
-
- string trapClass = trapEnt.ProjectileGetWeaponClassName()
- if ( trapClass == "" )
- return
-
- string scoreEvent
- if ( trapClass == "mp_weapon_satchel" )
- scoreEvent = "Destroyed_Satchel"
- else if ( trapClass == "mp_weapon_proximity_mine" )
- scoreEvent = "Destored_Proximity_Mine"
-
- if ( scoreEvent == "" )
- return
-
- AddPlayerScore( player, scoreEvent, trapEnt )
-}
-
-table function GetBulletPassThroughTargets( entity attacker, WeaponBulletHitParams hitParams )
-{
- //HACK requires code later
- table passThroughInfo = {
- endPos = null
- targetArray = []
- }
-
- TraceResults result
- array<entity> ignoreEnts = [ attacker, hitParams.hitEnt ]
-
- while ( true )
- {
- vector vec = ( hitParams.hitPos - hitParams.startPos ) * 1000
- ArrayRemoveInvalid( ignoreEnts )
- result = TraceLine( hitParams.startPos, vec, ignoreEnts, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE )
-
- if ( result.hitEnt == svGlobal.worldspawn )
- break
-
- ignoreEnts.append( result.hitEnt )
-
- if ( IsValidPassThroughTarget( result.hitEnt, attacker ) )
- passThroughInfo.targetArray.append( result.hitEnt )
- }
- passThroughInfo.endPos = result.endPos
-
- return passThroughInfo
-}
-#endif // SERVER
-
-bool function WeaponCanCrit( entity weapon )
-{
- // player sometimes has no weapon during titan exit, mantle, etc...
- if ( !weapon )
- return false
-
- return weapon.GetWeaponSettingBool( eWeaponVar.critical_hit )
-}
-
-
-#if SERVER
-bool function IsValidPassThroughTarget( entity target, entity attacker )
-{
- //Tied to PassThroughHack function remove when supported by code.
- if ( target == svGlobal.worldspawn )
- return false
-
- if ( !IsValid( target ) )
- return false
-
- if ( target.GetTeam() == attacker.GetTeam() )
- return false
-
- if ( target.GetTeam() != TEAM_IMC && target.GetTeam() != TEAM_MILITIA )
- return false
-
- return true
-}
-
-function PassThroughDamage( entity weapon, targetArray )
-{
- //Tied to PassThroughHack function remove when supported by code.
-
- int damageSourceID = weapon.GetDamageSourceID()
- entity owner = weapon.GetWeaponOwner()
-
- foreach ( ent in targetArray )
- {
- expect entity( ent )
-
- float distanceToTarget = Distance( weapon.GetOrigin(), ent.GetOrigin() )
- float damageToDeal = CalcWeaponDamage( owner, ent, weapon, distanceToTarget, 0 )
-
- ent.TakeDamage( damageToDeal, owner, weapon.GetWeaponOwner(), { damageSourceId = damageSourceID } )
- }
-}
-#endif // SERVER
-
-vector function GetVectorFromPositionToCrosshair( entity player, vector startPos )
-{
- Assert( IsValid( player ) )
-
- // See where we're looking
- vector traceStart = player.EyePosition()
- vector traceEnd = traceStart + ( player.GetViewVector() * 20000 )
- local ignoreEnts = [ player ]
- TraceResults traceResult = TraceLine( traceStart, traceEnd, ignoreEnts, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE )
-
- // Return vec from startPos to where we are looking
- vector vec = traceResult.endPos - startPos
- vec = Normalize( vec )
- return vec
-}
-
-/*
-function InitMissileForRandomDriftBasic( missile, startPos, startDir )
-{
- missile.s.RandomFloatRange <- RandomFloat( 1.0 )
- missile.s.startPos <- startPos
- missile.s.startDir <- startDir
-}
-*/
-
-function InitMissileForRandomDriftForVortexHigh( entity missile, vector startPos, vector startDir )
-{
- missile.InitMissileForRandomDrift( startPos, startDir, 8, 2.5, 0, 0, 100, 100 )
-}
-
-function InitMissileForRandomDriftForVortexLow( entity missile, vector startPos, vector startDir )
-{
- missile.InitMissileForRandomDrift( startPos, startDir, 0.3, 0.085, 0, 0, 0.5, 0.5 )
-}
-
-/*
-function InitMissileForRandomDrift( missile, startPos, startDir )
-{
- InitMissileForRandomDriftBasic( missile, startPos, startDir )
-
- missile.s.drift_windiness <- missile.ProjectileGetWeaponInfoFileKeyField( "projectile_drift_windiness" )
- missile.s.drift_intensity <- missile.ProjectileGetWeaponInfoFileKeyField( "projectile_drift_intensity" )
-
- missile.s.straight_time_min <- missile.ProjectileGetWeaponInfoFileKeyField( "projectile_straight_time_min" )
- missile.s.straight_time_max <- missile.ProjectileGetWeaponInfoFileKeyField( "projectile_straight_time_max" )
-
- missile.s.straight_radius_min <- missile.ProjectileGetWeaponInfoFileKeyField( "projectile_straight_radius_min" )
- if ( missile.s.straight_radius_min < 1 )
- missile.s.straight_radius_min = 1
- missile.s.straight_radius_max <- missile.ProjectileGetWeaponInfoFileKeyField( "projectile_straight_radius_max" )
- if ( missile.s.straight_radius_max < 1 )
- missile.s.straight_radius_max = 1
-}
-
-function SmoothRandom( x )
-{
- return 0.25 * (sin(x) + sin(x * 0.762) + sin(x * 0.363) + sin(x * 0.084))
-}
-
-function MissileRandomDrift( timeElapsed, timeStep, windiness, intensity )
-{
- // This function makes the missile go in a random direction.
- // Windiness is how frequently the missile changes direction.
- // Intensity is how strongly the missile steers in the direction it has chosen.
-
- local sampleTime = timeElapsed - timeStep * 0.5
-
- intensity *= timeStep
-
- local offset = self.s.RandomFloatRange * 1000
-
- local offsetx = intensity * SmoothRandom( offset + sampleTime * windiness )
- local offsety = intensity * SmoothRandom( offset * 2 + 100 + sampleTime * windiness )
-
- local right = self.GetRightVector()
- local up = self.GetUpVector()
-
- //DebugDrawLine( self.GetOrigin(), self.GetOrigin() + right * 100, 255,255,255, true, 0 )
- //DebugDrawLine( self.GetOrigin(), self.GetOrigin() + up * 100, 255,128,255, true, 0 )
-
- local dir = self.GetVelocity()
- local speed = Length( dir )
- dir = Normalize( dir )
- dir += right * offsetx
- dir += up * offsety
- dir = Normalize( dir )
- dir *= speed
-
- return dir
-}
-
-// designed to be called every frame (GetProjectileVelocity callback) on projectiles that are flying through the air
-function ApplyMissileControlledDrift( missile, timeElapsed, timeStep )
-{
- // If we have a target, don't do anything fancy; just let code do the homing behavior
- if ( missile.GetMissileTarget() )
- return missile.GetVelocity()
-
- local s = missile.s
- return MissileControlledDrift( timeElapsed, timeStep, s.drift_windiness, s.drift_intensity, s.straight_time_min, s.straight_time_max, s.straight_radius_min, s.straight_radius_max )
-}
-
-function MissileControlledDrift( timeElapsed, timeStep, windiness, intensity, pathTimeMin, pathTimeMax, pathRadiusMin, pathRadiusMax )
-{
- // Start with random drift.
- local vel = MissileRandomDrift( timeElapsed, timeStep, windiness, intensity )
-
- // Straighten our velocity back along our original path if we're below pathTimeMax.
- // Path time is how long it tries to stay on a straight path.
- // Path radius is how far it can get from its straight path.
- if ( timeElapsed < pathTimeMax )
- {
- local org = self.GetOrigin()
- local alongPathLen = self.s.startDir.Dot( org - self.s.startPos )
- local alongPathPos = self.s.startPos + self.s.startDir * alongPathLen
- local offPathOffset = org - alongPathPos
- local pathDist = Length( offPathOffset )
-
- local speed = Length( vel )
-
- local lerp = 1
- if ( timeElapsed > pathTimeMin )
- lerp = 1.0 - (timeElapsed - pathTimeMin) / (pathTimeMax - pathTimeMin)
-
- local pathRadius = pathRadiusMax + (pathRadiusMin - pathRadiusMax) * lerp
-
- // This circle shows the radius the missile is allowed to be in.
- //if ( IsServer() )
- // DebugDrawCircle( alongPathPos, VectorToAngles( AnglesToUp( VectorToAngles( self.s.startDir ) ) ), pathRadius, 255,255,255, true, 0.0 )
-
- local backToPathVel = offPathOffset * -1
- // Cap backToPathVel at speed
- if ( pathDist > pathRadius )
- backToPathVel *= speed / pathDist
- else
- backToPathVel *= speed / pathRadius
-
- if ( pathDist < pathRadius )
- {
- backToPathVel += self.s.startDir * (speed * (1.0 - pathDist / pathRadius))
- }
-
- //DebugDrawLine( org, org + vel * 0.1, 255,255,255, true, 0 )
- //DebugDrawLine( org, org + backToPathVel * intensity * lerp * 0.1, 128,255,128, true, 0 )
-
- vel += backToPathVel * (intensity * timeStep)
- vel = Normalize( vel )
- vel *= speed
- }
-
- return vel
-}
-*/
-
-#if SERVER
-function ClusterRocket_Detonate( entity rocket, vector normal )
-{
- entity owner = rocket.GetOwner()
- if ( !IsValid( owner ) )
- return
-
- int count
- float duration
- float range
-
- array mods = rocket.ProjectileGetMods()
- if ( mods.contains( "pas_northstar_cluster" ) )
- {
- count = CLUSTER_ROCKET_BURST_COUNT_BURN
- duration = PAS_NORTHSTAR_CLUSTER_ROCKET_DURATION
- range = CLUSTER_ROCKET_BURST_RANGE * 1.5
- }
- else
- {
- count = CLUSTER_ROCKET_BURST_COUNT
- duration = CLUSTER_ROCKET_DURATION
- range = CLUSTER_ROCKET_BURST_RANGE
- }
-
- if ( mods.contains( "fd_twin_cluster" ) )
- {
- count = int( count * 0.7 )
- duration *= 0.7
- }
- PopcornInfo popcornInfo
-
- popcornInfo.weaponName = "mp_titanweapon_dumbfire_rockets"
- popcornInfo.weaponMods = mods
- popcornInfo.damageSourceId = eDamageSourceId.mp_titanweapon_dumbfire_rockets
- popcornInfo.count = count
- popcornInfo.delay = CLUSTER_ROCKET_BURST_DELAY
- popcornInfo.offset = CLUSTER_ROCKET_BURST_OFFSET
- popcornInfo.range = range
- popcornInfo.normal = normal
- popcornInfo.duration = duration
- popcornInfo.groupSize = CLUSTER_ROCKET_BURST_GROUP_SIZE
- popcornInfo.hasBase = true
-
- thread StartClusterExplosions( rocket, owner, popcornInfo, CLUSTER_ROCKET_FX_TABLE )
-}
-
-
-function StartClusterExplosions( entity projectile, entity owner, PopcornInfo popcornInfo, customFxTable = null )
-{
- Assert( IsValid( owner ) )
- owner.EndSignal( "OnDestroy" )
-
- string weaponName = popcornInfo.weaponName
- float innerRadius
- float outerRadius
- int explosionDamage
- int explosionDamageHeavyArmor
-
- innerRadius = projectile.GetProjectileWeaponSettingFloat( eWeaponVar.explosion_inner_radius )
- outerRadius = projectile.GetProjectileWeaponSettingFloat( eWeaponVar.explosionradius )
- if ( owner.IsPlayer() )
- {
- explosionDamage = projectile.GetProjectileWeaponSettingInt( eWeaponVar.explosion_damage )
- explosionDamageHeavyArmor = projectile.GetProjectileWeaponSettingInt( eWeaponVar.explosion_damage_heavy_armor )
- }
- else
- {
- explosionDamage = projectile.GetProjectileWeaponSettingInt( eWeaponVar.npc_explosion_damage )
- explosionDamageHeavyArmor = projectile.GetProjectileWeaponSettingInt( eWeaponVar.npc_explosion_damage_heavy_armor )
- }
-
- local explosionDelay = projectile.ProjectileGetWeaponInfoFileKeyField( "projectile_explosion_delay" )
-
- if ( owner.IsPlayer() )
- owner.EndSignal( "OnDestroy" )
-
- vector origin = projectile.GetOrigin()
-
- vector rotateFX = Vector( 90,0,0 )
- entity placementHelper = CreateScriptMover()
- placementHelper.SetOrigin( origin )
- placementHelper.SetAngles( VectorToAngles( popcornInfo.normal ) )
- SetTeam( placementHelper, owner.GetTeam() )
-
- array<entity> players = GetPlayerArray()
- foreach ( player in players )
- {
- Remote_CallFunction_NonReplay( player, "SCB_AddGrenadeIndicatorForEntity", owner.GetTeam(), owner.GetEncodedEHandle(), placementHelper.GetEncodedEHandle(), outerRadius )
- }
-
- int particleSystemIndex = GetParticleSystemIndex( CLUSTER_BASE_FX )
- int attachId = placementHelper.LookupAttachment( "REF" )
- entity fx
-
- if ( popcornInfo.hasBase )
- {
- fx = StartParticleEffectOnEntity_ReturnEntity( placementHelper, particleSystemIndex, FX_PATTACH_POINT_FOLLOW, attachId )
- EmitSoundOnEntity( placementHelper, "Explo_ThermiteGrenade_Impact_3P" ) // TODO: wants a custom sound
- }
-
- OnThreadEnd(
- function() : ( fx, placementHelper )
- {
- if ( IsValid( fx ) )
- EffectStop( fx )
- placementHelper.Destroy()
- }
- )
-
- if ( explosionDelay )
- wait explosionDelay
-
- waitthread ClusterRocketBursts( origin, explosionDamage, explosionDamageHeavyArmor, innerRadius, outerRadius, owner, popcornInfo, customFxTable )
-
- if ( IsValid( projectile ) )
- projectile.Destroy()
-}
-
-
-//------------------------------------------------------------
-// ClusterRocketBurst() - does a "popcorn airburst" explosion effect over time around the origin. Total distance is based on popRangeBase
-// - returns the entity in case you want to parent it
-//------------------------------------------------------------
-function ClusterRocketBursts( vector origin, int damage, int damageHeavyArmor, float innerRadius, float outerRadius, entity owner, PopcornInfo popcornInfo, customFxTable = null )
-{
- owner.EndSignal( "OnDestroy" )
-
- // this ent remembers the weapon mods
- entity clusterExplosionEnt = CreateEntity( "info_target" )
- DispatchSpawn( clusterExplosionEnt )
-
- if ( popcornInfo.weaponMods.len() > 0 )
- clusterExplosionEnt.s.weaponMods <- popcornInfo.weaponMods
-
- clusterExplosionEnt.SetOwner( owner )
- clusterExplosionEnt.SetOrigin( origin )
-
- AI_CreateDangerousArea_Static( clusterExplosionEnt, null, outerRadius, TEAM_INVALID, true, true, origin )
-
- OnThreadEnd(
- function() : ( clusterExplosionEnt )
- {
- clusterExplosionEnt.Destroy()
- }
- )
-
- // No Damage - Only Force
- // Push players
- // Test LOS before pushing
- int flags = 11
- // create a blast that knocks pilots out of the way
- CreatePhysExplosion( origin, outerRadius, PHYS_EXPLOSION_LARGE, flags )
-
- int count = popcornInfo.groupSize
- for ( int index = 0; index < count; index++ )
- {
- thread ClusterRocketBurst( clusterExplosionEnt, origin, damage, damageHeavyArmor, innerRadius, outerRadius, owner, popcornInfo, customFxTable )
- WaitFrame()
- }
-
- wait CLUSTER_ROCKET_DURATION
-}
-
-function ClusterRocketBurst( entity clusterExplosionEnt, vector origin, damage, damageHeavyArmor, innerRadius, outerRadius, entity owner, PopcornInfo popcornInfo, customFxTable = null )
-{
- clusterExplosionEnt.EndSignal( "OnDestroy" )
- Assert( IsValid( owner ), "ClusterRocketBurst had invalid owner" )
-
- // first explosion always happens where you fired
- //int eDamageSource = popcornInfo.damageSourceId
- int numBursts = popcornInfo.count
- float popRangeBase = popcornInfo.range
- float popDelayBase = popcornInfo.delay
- float popDelayRandRange = popcornInfo.offset
- float duration = popcornInfo.duration
- int groupSize = popcornInfo.groupSize
-
- int counter = 0
- vector randVec
- float randRangeMod
- float popRange
- vector popVec
- vector popOri = origin
- float popDelay
- float colTrace
-
- float burstDelay = duration / ( numBursts / groupSize )
-
- vector clusterBurstOrigin = origin + (popcornInfo.normal * 8.0)
- entity clusterBurstEnt = CreateClusterBurst( clusterBurstOrigin )
-
- OnThreadEnd(
- function() : ( clusterBurstEnt )
- {
- if ( IsValid( clusterBurstEnt ) )
- {
- foreach ( fx in clusterBurstEnt.e.fxArray )
- {
- if ( IsValid( fx ) )
- fx.Destroy()
- }
- clusterBurstEnt.Destroy()
- }
- }
- )
-
- while ( IsValid( clusterBurstEnt ) && counter <= numBursts / popcornInfo.groupSize )
- {
- randVec = RandomVecInDome( popcornInfo.normal )
- randRangeMod = RandomFloat( 1.0 )
- popRange = popRangeBase * randRangeMod
- popVec = randVec * popRange
- popOri = origin + popVec
- popDelay = popDelayBase + RandomFloatRange( -popDelayRandRange, popDelayRandRange )
-
- colTrace = TraceLineSimple( origin, popOri, null )
- if ( colTrace < 1 )
- {
- popVec = popVec * colTrace
- popOri = origin + popVec
- }
-
- clusterBurstEnt.SetOrigin( clusterBurstOrigin )
-
- vector velocity = GetVelocityForDestOverTime( clusterBurstEnt.GetOrigin(), popOri, burstDelay - popDelay )
- clusterBurstEnt.SetVelocity( velocity )
-
- clusterBurstOrigin = popOri
-
- counter++
-
- wait burstDelay - popDelay
-
- Explosion(
- clusterBurstOrigin,
- owner,
- clusterExplosionEnt,
- damage,
- damageHeavyArmor,
- innerRadius,
- outerRadius,
- SF_ENVEXPLOSION_NOSOUND_FOR_ALLIES,
- clusterBurstOrigin,
- damage,
- damageTypes.explosive,
- popcornInfo.damageSourceId,
- customFxTable )
- }
-}
-
-
-entity function CreateClusterBurst( vector origin )
-{
- entity prop_physics = CreateEntity( "prop_physics" )
- prop_physics.SetValueForModelKey( $"models/weapons/bullets/projectile_rocket.mdl" )
- prop_physics.kv.spawnflags = 4 // 4 = SF_PHYSPROP_DEBRIS
- prop_physics.kv.fadedist = 2000
- prop_physics.kv.renderamt = 255
- prop_physics.kv.rendercolor = "255 255 255"
- prop_physics.kv.CollisionGroup = TRACE_COLLISION_GROUP_DEBRIS
-
- prop_physics.kv.minhealthdmg = 9999
- prop_physics.kv.nodamageforces = 1
- prop_physics.kv.inertiaScale = 1.0
-
- prop_physics.SetOrigin( origin )
- DispatchSpawn( prop_physics )
- prop_physics.SetModel( $"models/weapons/grenades/m20_f_grenade.mdl" )
-
- entity fx = PlayFXOnEntity( $"P_wpn_dumbfire_burst_trail", prop_physics )
- prop_physics.e.fxArray.append( fx )
-
- return prop_physics
-}
-#endif // SERVER
-
-vector function GetVelocityForDestOverTime( vector startPoint, vector endPoint, float duration )
-{
- const GRAVITY = 750
-
- float Vox = (endPoint.x - startPoint.x) / duration
- float Voy = (endPoint.y - startPoint.y) / duration
- float Voz = (endPoint.z + 0.5 * GRAVITY * duration * duration - startPoint.z) / duration
-
- return Vector( Vox, Voy, Voz )
-}
-
-vector function GetPlayerVelocityForDestOverTime( vector startPoint, vector endPoint, float duration )
-{
- // Same as above but accounts for player gravity setting not being 1.0
-
- float gravityScale = expect float( GetPlayerSettingsFieldForClassName( DEFAULT_PILOT_SETTINGS, "gravityscale" ) )
- float GRAVITY = 750 * gravityScale // adjusted for new gravity scale
-
- float Vox = (endPoint.x - startPoint.x) / duration
- float Voy = (endPoint.y - startPoint.y) / duration
- float Voz = (endPoint.z + 0.5 * GRAVITY * duration * duration - startPoint.z) / duration
-
- return Vector( Vox, Voy, Voz )
-}
-
-bool function HasLockedTarget( weapon )
-{
- if ( weapon.SmartAmmo_IsEnabled() )
- {
- local targets = weapon.SmartAmmo_GetTargets()
- if ( targets.len() > 0 )
- {
- foreach ( target in targets )
- {
- if ( target.fraction == 1 )
- return true
- }
- }
- }
- return false
-}
-
-function CanWeaponShootWhileRunning( entity weapon )
-{
- if ( "primary_fire_does_not_block_sprint" in weapon.s )
- return weapon.s.primary_fire_does_not_block_sprint
-
- if ( weapon.GetWeaponInfoFileKeyField( "primary_fire_does_not_block_sprint" ) == 1 )
- {
- weapon.s.primary_fire_does_not_block_sprint <- true
- return true
- }
-
- weapon.s.primary_fire_does_not_block_sprint <- false
- return false
-}
-
-#if CLIENT
-function ServerCallback_GuidedMissileDestroyed()
-{
- entity player = GetLocalViewPlayer()
-
- // guided missiles has not been updated to work with replays. added this if statement defensively just in case. - Roger
- if ( !( "missileInFlight" in player.s ) )
- return
-
- player.s.missileInFlight = false
-}
-
-function ServerCallback_AirburstIconUpdate( toggle )
-{
- entity player = GetLocalViewPlayer()
- entity cockpit = player.GetCockpit()
- if ( cockpit )
- {
- entity mainVGUI = cockpit.e.mainVGUI
- if ( mainVGUI )
- {
- if ( toggle )
- cockpit.s.offhandHud[OFFHAND_RIGHT].icon.SetImage( $"vgui/HUD/dpad_airburst_activate" )
- else
- cockpit.s.offhandHud[OFFHAND_RIGHT].icon.SetImage( $"vgui/HUD/dpad_airburst" )
- }
- }
-}
-
-bool function IsOwnerViewPlayerFullyADSed( entity weapon )
-{
- entity owner = weapon.GetOwner()
- if ( !IsValid( owner ) )
- return false
-
- if( !owner.IsPlayer() )
- return false
-
- if ( owner != GetLocalViewPlayer() )
- return false
-
- float zoomFrac = owner.GetZoomFrac()
- if ( zoomFrac < 1.0 )
- return false
-
- return true
-
-}
-#endif // CLIENT
-
-array<entity> function FireExpandContractMissiles( entity weapon, WeaponPrimaryAttackParams attackParams, vector attackPos, vector attackDir, int damageType, int explosionDamageType, shouldPredict, int rocketsPerShot, missileSpeed, launchOutAng, launchOutTime, launchInAng, launchInTime, launchInLerpTime, launchStraightLerpTime, applyRandSpread, int burstFireCountOverride = -1, debugDrawPath = false )
-{
- local missileVecs = GetExpandContractRocketTrajectories( weapon, attackParams.burstIndex, attackPos, attackDir, rocketsPerShot, launchOutAng, launchInAng, burstFireCountOverride )
- entity owner = weapon.GetWeaponOwner()
- array<entity> firedMissiles
-
- vector missileEndPos = owner.EyePosition() + ( attackDir * 5000 )
-
- for ( int i = 0; i < rocketsPerShot; i++ )
- {
- entity missile = weapon.FireWeaponMissile( attackPos, attackDir, missileSpeed, damageType, explosionDamageType, false, shouldPredict )
-
- if ( missile )
- {
- /*
- missile.s.flightData <- {
- launchOutVec = missileVecs[i].outward,
- launchOutTime = launchOutTime,
- launchInLerpTime = launchInLerpTime,
- launchInVec = missileVecs[i].inward,
- launchInTime = launchInTime,
- launchStraightLerpTime = launchStraightLerpTime,
- endPos = missileEndPos,
- applyRandSpread = applyRandSpread
- }
- */
-
- missile.InitMissileExpandContract( missileVecs[i].outward, missileVecs[i].inward, launchOutTime, launchInLerpTime, launchInTime, launchStraightLerpTime, missileEndPos, applyRandSpread )
-
- if ( IsServer() && debugDrawPath )
- thread DebugDrawMissilePath( missile )
-
- //InitMissileForRandomDrift( missile, attackPos, attackDir )
- missile.InitMissileForRandomDriftFromWeaponSettings( attackPos, attackDir )
-
- firedMissiles.append( missile )
- }
- }
-
- return firedMissiles
-}
-
-array<entity> function FireExpandContractMissiles_S2S( entity weapon, WeaponPrimaryAttackParams attackParams, vector attackPos, vector attackDir, shouldPredict, int rocketsPerShot, missileSpeed, launchOutAng, launchOutTime, launchInAng, launchInTime, launchInLerpTime, launchStraightLerpTime, applyRandSpread, int burstFireCountOverride = -1, debugDrawPath = false )
-{
- local missileVecs = GetExpandContractRocketTrajectories( weapon, attackParams.burstIndex, attackPos, attackDir, rocketsPerShot, launchOutAng, launchInAng, burstFireCountOverride )
- entity owner = weapon.GetWeaponOwner()
- array<entity> firedMissiles
-
- vector missileEndPos = attackPos + ( attackDir * 5000 )
-
- for ( int i = 0; i < rocketsPerShot; i++ )
- {
- entity missile = weapon.FireWeaponMissile( attackPos, attackDir, missileSpeed, DF_GIB | DF_IMPACT, damageTypes.explosive, false, shouldPredict )
- missile.SetOrigin( attackPos )//HACK why do I have to do this?
- if ( missile )
- {
- /*
- missile.s.flightData <- {
- launchOutVec = missileVecs[i].outward,
- launchOutTime = launchOutTime,
- launchInLerpTime = launchInLerpTime,
- launchInVec = missileVecs[i].inward,
- launchInTime = launchInTime,
- launchStraightLerpTime = launchStraightLerpTime,
- endPos = missileEndPos,
- applyRandSpread = applyRandSpread
- }
- */
-
- missile.InitMissileExpandContract( missileVecs[i].outward, missileVecs[i].inward, launchOutTime, launchInLerpTime, launchInTime, launchStraightLerpTime, missileEndPos, applyRandSpread )
-
- if ( IsServer() && debugDrawPath )
- thread DebugDrawMissilePath( missile )
-
- //InitMissileForRandomDrift( missile, attackPos, attackDir )
- missile.InitMissileForRandomDriftFromWeaponSettings( attackPos, attackDir )
-
- firedMissiles.append( missile )
- }
- }
-
- return firedMissiles
-}
-
-function GetExpandContractRocketTrajectories( entity weapon, int burstIndex, vector attackPos, vector attackDir, int rocketsPerShot, launchOutAng, launchInAng, int burstFireCount = -1 )
-{
- bool DEBUG_DRAW_MATH = false
-
- if ( burstFireCount == -1 )
- burstFireCount = weapon.GetWeaponBurstFireCount()
-
- local additionalRotation = ( ( 360.0 / rocketsPerShot ) / burstFireCount ) * burstIndex
- //printt( "burstIndex:", burstIndex )
- //printt( "rocketsPerShot:", rocketsPerShot )
- //printt( "burstFireCount:", burstFireCount )
-
- vector ang = VectorToAngles( attackDir )
- vector forward = AnglesToForward( ang )
- vector right = AnglesToRight( ang )
- vector up = AnglesToUp( ang )
-
- if ( DEBUG_DRAW_MATH )
- DebugDrawLine( attackPos, attackPos + ( forward * 1000 ), 255, 0, 0, true, 30.0 )
-
- // Create points on circle
- float offsetAng = 360.0 / rocketsPerShot
- for ( int i = 0; i < rocketsPerShot; i++ )
- {
- local a = offsetAng * i + additionalRotation
- vector vec = Vector( 0, 0, 0 )
- vec += up * deg_sin( a )
- vec += right * deg_cos( a )
-
- if ( DEBUG_DRAW_MATH )
- DebugDrawLine( attackPos, attackPos + ( vec * 50 ), 10, 10, 10, true, 30.0 )
- }
-
- // Create missile points
- vector x = right * deg_sin( launchOutAng )
- vector y = up * deg_sin( launchOutAng )
- vector z = forward * deg_cos( launchOutAng )
- vector rx = right * deg_sin( launchInAng )
- vector ry = up * deg_sin( launchInAng )
- vector rz = forward * deg_cos( launchInAng )
- local missilePoints = []
- for ( int i = 0; i < rocketsPerShot; i++ )
- {
- local points = {}
-
- // Outward vec
- local a = offsetAng * i + additionalRotation
- float s = deg_sin( a )
- float c = deg_cos( a )
- vector vecOut = z + x * c + y * s
- vecOut = Normalize( vecOut )
- points.outward <- vecOut
-
- // Inward vec
- vector vecIn = rz + rx * c + ry * s
- points.inward <- vecIn
-
- // Add to array
- missilePoints.append( points )
-
- if ( DEBUG_DRAW_MATH )
- {
- DebugDrawLine( attackPos, attackPos + ( vecOut * 50 ), 255, 255, 0, true, 30.0 )
- DebugDrawLine( attackPos + vecOut * 50, attackPos + vecOut * 50 + ( vecIn * 50 ), 255, 0, 255, true, 30.0 )
- }
- }
-
- return missilePoints
-}
-
-function DebugDrawMissilePath( entity missile )
-{
- EndSignal( missile, "OnDestroy" )
- vector lastPos = missile.GetOrigin()
- while ( true )
- {
- WaitFrame()
- if ( !IsValid( missile ) )
- return
- DebugDrawLine( lastPos, missile.GetOrigin(), 0, 255, 0, true, 20.0 )
- lastPos = missile.GetOrigin()
- }
-}
-
-
-function RegenerateOffhandAmmoOverTime( entity weapon, float rechargeTime, int maxAmmo, int offhandIndex )
-{
- weapon.Signal( "RegenAmmo" )
- weapon.EndSignal( "RegenAmmo" )
- weapon.EndSignal( "OnDestroy" )
-
- #if CLIENT
- entity weaponOwner = weapon.GetWeaponOwner()
- if ( IsValid( weaponOwner ) && weaponOwner.IsPlayer() )
- {
- entity cockpit = weaponOwner.GetCockpit()
- if ( IsValid( cockpit ) )
- {
- cockpit.s.offhandHud[offhandIndex].bar.SetBarProgressSource( ProgressSource.PROGRESS_SOURCE_SCRIPTED )
- cockpit.s.offhandHud[offhandIndex].bar.SetBarProgressRemap( 0.0, 1.0, 0.0, 1.0 )
- cockpit.s.offhandHud[offhandIndex].bar.SetBarProgressAndRate( 1.0 / maxAmmo , 1 / ( rechargeTime * maxAmmo ) )
- }
- }
- #endif
-
- if ( !( "totalChargeTime" in weapon.s ) )
- weapon.s.totalChargeTime <- rechargeTime
-
- if ( !( "nextChargeTime" in weapon.s ) )
- weapon.s.nextChargeTime <- null
-
- for ( ;; )
- {
- weapon.s.nextChargeTime = rechargeTime + Time()
-
- wait rechargeTime
-
- if ( IsServer() )
- {
- int max = maxAmmo
- int weaponMax = weapon.GetWeaponPrimaryClipCountMax()
- if ( weaponMax < max )
- max = weaponMax
-
- int ammo = weapon.GetWeaponPrimaryClipCount()
- if ( ammo < max )
- weapon.SetWeaponPrimaryClipCount( ammo + 1 )
- }
- }
-}
-
-bool function IsPilotShotgunWeapon( string weaponName )
-{
- return GetWeaponInfoFileKeyField_Global( weaponName, "weaponSubClass" ) == "shotgun"
-}
-
-array<string> function GetWeaponBurnMods( string weaponClassName )
-{
- array<string> burnMods = []
- array<string> mods = GetWeaponMods_Global( weaponClassName )
- string prefix = "burn_mod"
- foreach ( mod in mods )
- {
- if ( mod.find( prefix ) == 0 )
- burnMods.append( mod )
- }
-
- return burnMods
-}
-
-int function TEMP_GetDamageFlagsFromProjectile( entity projectile )
-{
- var damageFlagsString = projectile.ProjectileGetWeaponInfoFileKeyField( "damage_flags" )
- if ( damageFlagsString == null )
- return 0
- expect string( damageFlagsString )
-
- return TEMP_GetDamageFlagsFromString( damageFlagsString )
-}
-
-int function TEMP_GetDamageFlagsFromString( string damageFlagsString )
-{
- int damageFlags = 0
-
- array<string> damageFlagTokens = split( damageFlagsString, "|" )
- foreach ( token in damageFlagTokens )
- {
- damageFlags = damageFlags | getconsttable()[strip(token)]
- }
-
- return damageFlags
-}
-
-#if SERVER
-function PROTO_InitTrackedProjectile( entity projectile )
-{
- // HACK: accessing ProjectileGetWeaponInfoFileKeyField or ProjectileGetWeaponClassName during CodeCallback_OnSpawned causes a code assert
- projectile.EndSignal( "OnDestroy" )
- WaitFrame()
-
- entity owner = projectile.GetOwner()
-
- if ( !IsValid( owner ) || !owner.IsPlayer() )
- return
-
- int maxDeployed = projectile.GetProjectileWeaponSettingInt( eWeaponVar.projectile_max_deployed )
- if ( maxDeployed != 0 )
- {
- AddToScriptManagedEntArray( owner.s.activeTrapArrayId, projectile )
-
- array<entity> traps = GetScriptManagedEntArray( owner.s.activeTrapArrayId )
- array<entity> sameTypeTrapEnts
- foreach ( ent in traps )
- {
- if ( ent.ProjectileGetWeaponClassName() != projectile.ProjectileGetWeaponClassName() )
- continue
-
- sameTypeTrapEnts.append( ent )
- }
-
- int numToDestroy = sameTypeTrapEnts.len() - maxDeployed
- if ( numToDestroy > 0 )
- {
- sameTypeTrapEnts.sort( CompareCreation )
- foreach ( ent in sameTypeTrapEnts )
- {
- ent.Destroy()
- numToDestroy--
-
- if ( !numToDestroy )
- break
- }
- }
- }
-}
-
-
-function PROTO_CleanupTrackedProjectiles( entity player )
-{
- array<entity> traps = GetScriptManagedEntArray( player.s.activeTrapArrayId )
- foreach ( ent in traps )
- {
- ent.Destroy()
- }
-}
-
-int function CompareCreation( entity a, entity b )
-{
- if ( a.GetProjectileCreationTime() > b.GetProjectileCreationTime() )
- return 1
-
- return -1
-}
-
-int function CompareCreationReverse( entity a, entity b )
-{
- if ( a.GetProjectileCreationTime() > b.GetProjectileCreationTime() )
- return 1
-
- return -1
-}
-
-void function PROTO_TrackedProjectile_OnPlayerRespawned( entity player )
-{
- thread PROTO_TrackedProjectile_OnPlayerRespawned_Internal( player )
-}
-
-void function PROTO_TrackedProjectile_OnPlayerRespawned_Internal( entity player )
-{
- player.EndSignal( "OnDeath" )
-
- if ( player.s.inGracePeriod )
- player.WaitSignal( "GracePeriodDone" )
-
- entity ordnance = player.GetOffhandWeapon( OFFHAND_ORDNANCE )
-
- array<entity> traps = GetScriptManagedEntArray( player.s.activeTrapArrayId )
- foreach ( ent in traps )
- {
- if ( ordnance && ent.ProjectileGetWeaponClassName() == ordnance.GetWeaponClassName() )
- continue
-
- ent.Destroy()
- }
-}
-
-function PROTO_PlayTrapLightEffect( entity ent, string tag, int team )
-{
- asset ownerFx = ent.ProjectileGetWeaponInfoFileKeyFieldAsset( "trap_warning_owner_fx" )
- if ( ownerFx != $"" )
- {
- entity ownerFxEnt = CreateServerEffect_Owner( ownerFx, ent.GetOwner() )
- SetServerEffectControlPoint( ownerFxEnt, 0, FRIENDLY_COLOR )
- StartServerEffectOnEntity( ownerFxEnt, ent, tag )
- }
-
- asset friendlyFx = ent.ProjectileGetWeaponInfoFileKeyFieldAsset( "trap_warning_friendly_fx" )
- if ( friendlyFx != $"" )
- {
- entity friendlyFxEnt = CreateServerEffect_Friendly( friendlyFx, team )
- SetServerEffectControlPoint( friendlyFxEnt, 0, FRIENDLY_COLOR_FX )
- StartServerEffectOnEntity( friendlyFxEnt, ent, tag )
- }
-
- asset enemyFx = ent.ProjectileGetWeaponInfoFileKeyFieldAsset( "trap_warning_enemy_fx" )
- if ( enemyFx != $"" )
- {
- entity enemyFxEnt = CreateServerEffect_Enemy( enemyFx, team )
- SetServerEffectControlPoint( enemyFxEnt, 0, ENEMY_COLOR_FX )
- StartServerEffectOnEntity( enemyFxEnt, ent, tag )
- }
-}
-
-string ornull function GetCooldownBeepPrefix( weapon )
-{
- var reloadBeepPrefix = weapon.GetWeaponInfoFileKeyField( "cooldown_sound_prefix" )
- if ( reloadBeepPrefix == null )
- return null
-
- expect string( reloadBeepPrefix )
-
- return reloadBeepPrefix
-}
-
-void function PROTO_DelayCooldown( entity weapon )
-{
- weapon.s.nextCooldownTime = Time() + weapon.s.cooldownDelay
-}
-
-string function GetBeepSuffixForAmmo( int currentAmmo, int maxAmmo )
-{
- float frac = float( currentAmmo ) / float( maxAmmo )
-
- if ( frac >= 1.0 )
- return "_full"
-
- if ( frac >= 0.25 )
- return ""
-
- return "_low"
-}
-
-#endif //SERVER
-
-bool function PROTO_CanPlayerDeployWeapon( entity player )
-{
- if ( player.IsPhaseShifted() )
- return false
-
- if ( player.ContextAction_IsActive() == true )
- {
- if ( player.IsZiplining() )
- return true
- else
- return false
- }
-
- return true
-}
-
-#if SERVER
-void function PROTO_FlakCannonMissiles( entity projectile, float speed )
-{
- projectile.EndSignal( "OnDestroy" )
-
- float radius = projectile.GetProjectileWeaponSettingFloat( eWeaponVar.explosionradius )
- vector velocity = projectile.GetVelocity()
- vector currentPos = projectile.GetOrigin()
- int team = projectile.GetTeam()
-
- float waitTime = 0.1
- float distanceInterval = speed * waitTime
- int forwardDistanceChecks = int( ceil( distanceInterval / radius ) )
- bool forceExplosion = false
- while ( forceExplosion == false )
- {
- currentPos = projectile.GetOrigin()
- for ( int i = 0; i < forwardDistanceChecks; i++ )
- {
- float frac = float( i ) / float (forwardDistanceChecks )
- if ( PROTO_FlakCannon_HasNearbyEnemies( currentPos + velocity * waitTime * frac , team, radius ) )
- {
- if ( i == 0 )
- {
- forceExplosion = true
- break
- }
- else
- {
- projectile.SetVelocity( velocity * ( frac - 0.05 ) )
- break
- }
- }
- }
-
- if ( forceExplosion == false )
- wait waitTime
- }
-
- projectile.MissileExplode()
-}
-
-bool function PROTO_FlakCannon_HasNearbyEnemies( vector origin, int team, float radius )
-{
- float worldSpaceCenterBuffer = 200
-
- array<entity> guys = GetPlayerArrayEx( "any", TEAM_ANY, team, origin, radius + worldSpaceCenterBuffer )
- foreach ( guy in guys )
- {
- if ( IsAlive( guy ) && Distance( origin, guy.GetWorldSpaceCenter() ) < radius )
- return true
- }
-
- array<entity> ai = GetNPCArrayEx( "any", TEAM_ANY, team, origin, radius + worldSpaceCenterBuffer )
- foreach ( guy in ai )
- {
- if ( IsAlive( guy ) && Distance( origin, guy.GetWorldSpaceCenter() ) < radius )
- return true
- }
-
- return false
-}
-#endif // #if SERVER
-
-void function GiveEMPStunStatusEffects( entity ent, float duration, float fadeoutDuration = 0.5, float slowTurn = EMP_SEVERITY_SLOWTURN, float slowMove = EMP_SEVERITY_SLOWMOVE)
-{
- entity target = ent.IsTitan() ? ent.GetTitanSoul() : ent
- int slowEffect = StatusEffect_AddTimed( target, eStatusEffect.turn_slow, slowTurn, duration, fadeoutDuration )
- int turnEffect = StatusEffect_AddTimed( target, eStatusEffect.move_slow, slowMove, duration, fadeoutDuration )
-
- #if SERVER
- if ( ent.IsPlayer() )
- {
- ent.p.empStatusEffectsToClearForPhaseShift.append( slowEffect )
- ent.p.empStatusEffectsToClearForPhaseShift.append( turnEffect )
- }
- #endif
-}
-
-#if DEV
-string ornull function FindEnumNameForValue( table searchTable, int searchVal )
-{
- foreach( string keyname, int value in searchTable )
- {
- if ( value == searchVal )
- return keyname;
- }
- return null
-}
-
-void function DevPrintAllStatusEffectsOnEnt( entity ent )
-{
- printt( "Effects:", ent )
- array<float> effects = StatusEffect_GetAll( ent )
- int length = effects.len()
- int found = 0;
- for ( int idx = 0; idx < length; idx++ )
- {
- float severity = effects[idx];
- if ( severity <= 0.0 )
- continue
- string ornull name = FindEnumNameForValue( eStatusEffect, idx )
- Assert( name )
- expect string( name )
- printt( " eStatusEffect." + name + ": " + severity )
- found++;
- }
- printt( found + " effects active.\n" );
-}
-#endif // #if DEV
-
-array<entity> function GetPrimaryWeapons( entity player )
-{
- array<entity> primaryWeapons
- array<entity> weapons = player.GetMainWeapons()
- foreach ( weaponEnt in weapons )
- {
- int weaponType = weaponEnt.GetWeaponType()
- if ( weaponType == WT_SIDEARM || weaponType == WT_ANTITITAN )
- continue;
-
- primaryWeapons.append( weaponEnt )
- }
- return primaryWeapons
-}
-
-array<entity> function GetSidearmWeapons( entity player )
-{
- array<entity> sidearmWeapons
- array<entity> weapons = player.GetMainWeapons()
- foreach ( weaponEnt in weapons )
- {
- if ( weaponEnt.GetWeaponType() != WT_SIDEARM )
- continue
-
- sidearmWeapons.append( weaponEnt )
- }
- return sidearmWeapons
-}
-
-array<entity> function GetATWeapons( entity player )
-{
- array<entity> atWeapons
- array<entity> weapons = player.GetMainWeapons()
- foreach ( weaponEnt in weapons )
- {
- if ( weaponEnt.GetWeaponType() != WT_ANTITITAN )
- continue
-
- atWeapons.append( weaponEnt )
- }
- return atWeapons
-}
-
-entity function GetPlayerFromTitanWeapon( entity weapon )
-{
- entity titan = weapon.GetWeaponOwner()
- entity player
-
- if ( titan == null )
- return null
-
- if ( !titan.IsPlayer() )
- player = titan.GetBossPlayer()
- else
- player = titan
-
- return player
-}
-
-
-const asset CHARGE_SHOT_PROJECTILE = $"models/weapons/bullets/temp_triple_threat_projectile_large.mdl"
-
-const asset CHARGE_EFFECT_1P = $"P_ordnance_charge_st_FP" // $"P_wpn_defender_charge_FP"
-const asset CHARGE_EFFECT_3P = $"P_ordnance_charge_st" // $"P_wpn_defender_charge"
-const asset CHARGE_EFFECT_DLIGHT = $"defender_charge_CH_dlight"
-
-const string CHARGE_SOUND_WINDUP_1P = "Weapon_ChargeRifle_WindUp_1P"
-const string CHARGE_SOUND_WINDUP_3P = "Weapon_ChargeRifle_WindUp_3P"
-const string CHARGE_SOUND_WINDDOWN_1P = "Weapon_ChargeRifle_WindDown_1P"
-const string CHARGE_SOUND_WINDDOWN_3P = "Weapon_ChargeRifle_WindDown_3P"
-
-void function ChargeBall_Precache()
-{
-#if SERVER
- PrecacheModel( CHARGE_SHOT_PROJECTILE )
- PrecacheEffect( CHARGE_EFFECT_1P )
- PrecacheEffect( CHARGE_EFFECT_3P )
-#endif // #if SERVER
-}
-
-void function ChargeBall_FireProjectile( entity weapon, vector position, vector direction, bool shouldPredict )
-{
- weapon.EmitWeaponNpcSound( LOUD_WEAPON_AI_SOUND_RADIUS_MP, 0.2 )
-
- entity owner = weapon.GetWeaponOwner()
- const float MISSILE_SPEED = 1200.0
- const int CONTACT_DAMAGE_TYPES = (damageTypes.projectileImpact | DF_DOOM_FATALITY)
- const int EXPLOSION_DAMAGE_TYPES = damageTypes.explosive
- const bool DO_POPUP = false
-
- if ( shouldPredict )
- {
- entity missile = weapon.FireWeaponMissile( position, direction, MISSILE_SPEED, CONTACT_DAMAGE_TYPES, EXPLOSION_DAMAGE_TYPES, DO_POPUP, shouldPredict )
- if ( missile )
- {
- EmitSoundOnEntity( owner, "ShoulderRocket_Cluster_Fire_3P" )
- missile.SetModel( CHARGE_SHOT_PROJECTILE )
-#if CLIENT
- const ROCKETEER_MISSILE_EXPLOSION = $"xo_exp_death"
- const ROCKETEER_MISSILE_SHOULDER_FX = $"wpn_mflash_xo_rocket_shoulder_FP"
- entity owner = weapon.GetWeaponOwner()
- vector origin = owner.OffsetPositionFromView( Vector(0, 0, 0), Vector(25, -25, 15) )
- vector angles = owner.CameraAngles()
- StartParticleEffectOnEntityWithPos( owner, GetParticleSystemIndex( ROCKETEER_MISSILE_SHOULDER_FX ), FX_PATTACH_EYES_FOLLOW, -1, origin, angles )
-#else // #if CLIENT
- missile.SetProjectileImpactDamageOverride( 1440 )
- missile.kv.damageSourceId = eDamageSourceId.charge_ball
-#endif // #else // #if CLIENT
- }
- }
-}
-
-bool function ChargeBall_ChargeBegin( entity weapon, string tagName )
-{
-#if CLIENT
- if ( InPrediction() && !IsFirstTimePredicted() )
- return true
-#endif // #if CLIENT
-
- weapon.w.statusEffects.append( StatusEffect_AddEndless( weapon.GetWeaponOwner(), eStatusEffect.move_slow, 0.6 ) )
- weapon.w.statusEffects.append( StatusEffect_AddEndless( weapon.GetWeaponOwner(), eStatusEffect.turn_slow, 0.35 ) )
-
- weapon.PlayWeaponEffect( CHARGE_EFFECT_1P, CHARGE_EFFECT_3P, tagName )
- weapon.PlayWeaponEffect( $"", CHARGE_EFFECT_DLIGHT, tagName )
-
-#if SERVER
- StopSoundOnEntity( weapon, CHARGE_SOUND_WINDDOWN_3P )
- entity weaponOwner = weapon.GetWeaponOwner()
- if ( IsValid( weaponOwner ) )
- {
- if ( weaponOwner.IsPlayer() )
- EmitSoundOnEntityExceptToPlayer( weapon, weaponOwner, CHARGE_SOUND_WINDUP_3P )
- else
- EmitSoundOnEntity( weapon, CHARGE_SOUND_WINDUP_3P )
- }
-#else
- StopSoundOnEntity( weapon, CHARGE_SOUND_WINDDOWN_1P )
- EmitSoundOnEntity( weapon, CHARGE_SOUND_WINDUP_1P )
-#endif
-
- return true
-}
-
-void function ChargeBall_ChargeEnd( entity weapon )
-{
-#if CLIENT
- if ( InPrediction() && !IsFirstTimePredicted() )
- return
-#endif
-
- if ( IsValid( weapon.GetWeaponOwner() ) )
- {
- #if CLIENT
- if ( InPrediction() && IsFirstTimePredicted() )
- {
- #endif
-
- foreach ( effect in weapon.w.statusEffects )
- {
- StatusEffect_Stop( weapon.GetWeaponOwner(), effect )
- }
-
- #if CLIENT
- }
- #endif
- }
-
-#if SERVER
- StopSoundOnEntity( weapon, CHARGE_SOUND_WINDUP_3P )
- entity weaponOwner = weapon.GetWeaponOwner()
- if ( IsValid( weaponOwner ) )
- {
- if ( weaponOwner.IsPlayer() )
- EmitSoundOnEntityExceptToPlayer( weapon, weaponOwner, CHARGE_SOUND_WINDDOWN_3P )
- else
- EmitSoundOnEntity( weapon, CHARGE_SOUND_WINDDOWN_3P )
- }
-#else
- StopSoundOnEntity( weapon, CHARGE_SOUND_WINDUP_1P )
- EmitSoundOnEntity( weapon, CHARGE_SOUND_WINDDOWN_1P )
-#endif
-
- ChargeBall_StopChargeEffects( weapon )
-}
-
-void function ChargeBall_StopChargeEffects( entity weapon )
-{
- Assert( IsValid( weapon ) )
- // weapon.StopWeaponEffect( CHARGE_EFFECT_1P, CHARGE_EFFECT_3P )
- // weapon.StopWeaponEffect( CHARGE_EFFECT_3P, CHARGE_EFFECT_1P )
- // weapon.StopWeaponEffect( CHARGE_EFFECT_DLIGHT, CHARGE_EFFECT_DLIGHT )
- thread HACK_Deplayed_ChargeBall_StopChargeEffects( weapon )
-}
-
-void function HACK_Deplayed_ChargeBall_StopChargeEffects( entity weapon )
-{
- weapon.EndSignal( "OnDestroy" )
- wait 0.2
- weapon.StopWeaponEffect( CHARGE_EFFECT_1P, CHARGE_EFFECT_3P )
- weapon.StopWeaponEffect( CHARGE_EFFECT_3P, CHARGE_EFFECT_1P )
- weapon.StopWeaponEffect( CHARGE_EFFECT_DLIGHT, CHARGE_EFFECT_DLIGHT )
-}
-
-float function ChargeBall_GetChargeTime()
-{
- return 1.05
-}
-
-#if SERVER
-void function GivePlayerAmpedWeapon( entity player, string weaponName )
-{
- array<entity> weapons = player.GetMainWeapons()
- int numWeapons = weapons.len()
- if ( numWeapons == 0 )
- return
-
- //Figure out what weapon to take away.
- //This is more complicated than it should be because of rules of what weapons can be in what slots, e.g. your anti-titan weapon can't be replaced by non anti-titan weapons
- if ( HasWeapon( player, weaponName ) )
- {
- //Simplest case:
- //Take away the currently existing version of the weapon you already have.
- player.TakeWeaponNow( weaponName )
- }
- else
- {
- bool ampedWeaponIsAntiTitan = GetWeaponInfoFileKeyField_Global( weaponName, "weaponType" ) == "anti_titan"
- if ( ampedWeaponIsAntiTitan )
- {
- foreach( weapon in weapons )
- {
- string currentWeaponClassName = weapon.GetWeaponClassName()
- if ( GetWeaponInfoFileKeyField_Global( currentWeaponClassName, "weaponType" ) == "anti_titan" )
- {
- player.TakeWeaponNow( currentWeaponClassName )
- break
- }
- }
-
- unreachable //We had no anti-titan weapon? Shouldn't ever be possible
-
- }
- else
- {
- string currentActiveWeaponClassName = player.GetActiveWeapon().GetWeaponClassName()
- if ( ShouldReplaceWeaponInFirstSlot( player, currentActiveWeaponClassName ) )
- {
- //Current weapon is anti_titan, but amped weapon we are trying to give is not. Just replace the weapon that is in the first slot.
- //Assumes that weapon in first slot is not an anti-titan weapon
- //We could get even fancier and look to see if the amped weapon is a primary weapon or a sidearm and replace the slot accordingly, but
- //that makes it more complicated, plus there are cases where you can have no primary weapons/no side arms etc
- string firstWeaponClassName = weapons[ 0 ].GetWeaponClassName()
- Assert( GetWeaponInfoFileKeyField_Global( firstWeaponClassName, "weaponType" ) != "anti_titan" )
- player.TakeWeaponNow( firstWeaponClassName )
- }
- else
- {
- player.TakeWeaponNow( currentActiveWeaponClassName )
- }
- }
- }
-
- array<string> burnMods = GetWeaponBurnMods( weaponName )
- entity ampedWeapon = player.GiveWeapon( weaponName, burnMods )
- ampedWeapon.SetWeaponPrimaryClipCount( ampedWeapon.GetWeaponPrimaryClipCountMax() ) //Needed for weapons that give a mod with extra clip size
-}
-
-bool function ShouldReplaceWeaponInFirstSlot( entity player, string currentActiveWeaponClassName )
-{
- if ( GetWeaponInfoFileKeyField_Global( currentActiveWeaponClassName, "weaponType" ) == "anti_titan" ) //Active weapon is anti-titan weapon. Can't replace anti-titan weapon slot with non-anti-titan weapon
- return true
-
- if ( currentActiveWeaponClassName == player.GetOffhandWeapon( OFFHAND_ORDNANCE ).GetWeaponClassName() )
- return true
-
- return false
-
-}
-
-void function GivePlayerAmpedWeaponAndSetAsActive( entity player, string weaponName )
-{
- GivePlayerAmpedWeapon( player, weaponName )
- player.SetActiveWeaponByName( weaponName )
-}
-
-void function ReplacePlayerOffhand( entity player, string offhandName, array<string> mods = [] )
-{
- player.TakeOffhandWeapon( OFFHAND_SPECIAL )
- player.GiveOffhandWeapon( offhandName, OFFHAND_SPECIAL, mods )
-}
-
-void function ReplacePlayerOrdnance( entity player, string ordnanceName, array<string> mods = [] )
-{
- player.TakeOffhandWeapon( OFFHAND_ORDNANCE )
- player.GiveOffhandWeapon( ordnanceName, OFFHAND_ORDNANCE, mods )
-}
-
-void function PAS_CooldownReduction_OnKill( entity victim, entity attacker, var damageInfo )
-{
- if ( !IsAlive( attacker ) || !IsPilot( attacker ) )
- return
-
- array<string> weaponMods = GetWeaponModsFromDamageInfo( damageInfo )
-
- if ( GetCurrentPlaylistVarInt( "featured_mode_tactikill", 0 ) > 0 )
- {
- entity weapon = attacker.GetOffhandWeapon( OFFHAND_LEFT )
-
- switch ( GetWeaponInfoFileKeyField_Global( weapon.GetWeaponClassName(), "cooldown_type" ) )
- {
- case "grapple":
- attacker.SetSuitGrapplePower( attacker.GetSuitGrapplePower() + 100 )
- break
-
- case "ammo":
- case "ammo_instant":
- case "ammo_deployed":
- case "ammo_timed":
- int maxAmmo = weapon.GetWeaponPrimaryClipCountMax()
- weapon.SetWeaponPrimaryClipCountNoRegenReset( maxAmmo )
- break
-
- case "chargeFrac":
- weapon.SetWeaponChargeFraction( 0 )
- break
-
- // case "mp_ability_ground_slam":
- // break
-
- default:
- Assert( false, weapon.GetWeaponClassName() + " needs to be updated to support cooldown_type setting" )
- break
- }
- }
- else
- {
- if ( !PlayerHasPassive( attacker, ePassives.PAS_CDR_ON_KILL ) && !weaponMods.contains( "tactical_cdr_on_kill" ) )
- return
-
- entity weapon = attacker.GetOffhandWeapon( OFFHAND_LEFT )
-
- switch ( GetWeaponInfoFileKeyField_Global( weapon.GetWeaponClassName(), "cooldown_type" ) )
- {
- case "grapple":
- attacker.SetSuitGrapplePower( attacker.GetSuitGrapplePower() + 25 )
- break
-
- case "ammo":
- case "ammo_instant":
- case "ammo_deployed":
- case "ammo_timed":
- int maxAmmo = weapon.GetWeaponPrimaryClipCountMax()
- weapon.SetWeaponPrimaryClipCountNoRegenReset( min( maxAmmo, weapon.GetWeaponPrimaryClipCount() + ( maxAmmo / 4 ) ) )
- break
-
- case "chargeFrac":
- weapon.SetWeaponChargeFraction( max( 0, weapon.GetWeaponChargeFraction() - 0.25 ) )
- break
-
- // case "mp_ability_ground_slam":
- // break
-
- default:
- Assert( false, weapon.GetWeaponClassName() + " needs to be updated to support cooldown_type setting" )
- break
- }
- }
-}
-
-void function DisableWeapons( entity player, array<string> excludeNames )
-{
- array<entity> weapons = GetPlayerWeapons( player, excludeNames )
- foreach ( weapon in weapons )
- weapon.AllowUse( false )
-}
-
-void function EnableWeapons( entity player, array<string> excludeNames )
-{
- array<entity> weapons = GetPlayerWeapons( player, excludeNames )
- foreach ( weapon in weapons )
- weapon.AllowUse( true )
-}
-
-array<entity> function GetPlayerWeapons( entity player, array<string> excludeNames )
-{
- array<entity> weapons = player.GetMainWeapons()
- weapons.extend( player.GetOffhandWeapons() )
-
- for ( int idx = weapons.len() - 1; idx > 0; idx-- )
- {
- foreach ( excludeName in excludeNames )
- {
- if ( weapons[idx].GetWeaponClassName() == excludeName )
- weapons.remove( idx )
- }
- }
-
- return weapons
-}
-
-void function WeaponAttackWave( entity ent, int projectileCount, entity inflictor, vector pos, vector dir, bool functionref( entity, int, entity, entity, vector, vector, int ) waveFunc )
-{
- ent.EndSignal( "OnDestroy" )
-
- entity weapon
- entity projectile
- int maxCount
- float step
- entity owner
- int damageNearValueTitanArmor
- int count = 0
- array<vector> positions = []
- vector lastDownPos
- bool firstTrace = true
-
- dir = <dir.x, dir.y, 0.0>
- dir = Normalize( dir )
- vector angles = VectorToAngles( dir )
-
- if ( ent.IsProjectile() )
- {
- projectile = ent
- string chargedPrefix = ""
- if ( ent.proj.isChargedShot )
- chargedPrefix = "charge_"
-
- maxCount = expect int( ent.ProjectileGetWeaponInfoFileKeyField( chargedPrefix + "wave_max_count" ) )
- step = expect float( ent.ProjectileGetWeaponInfoFileKeyField( chargedPrefix + "wave_step_dist" ) )
- owner = ent.GetOwner()
- damageNearValueTitanArmor = projectile.GetProjectileWeaponSettingInt( eWeaponVar.damage_near_value_titanarmor )
- }
- else
- {
- weapon = ent
- maxCount = expect int( ent.GetWeaponInfoFileKeyField( "wave_max_count" ) )
- step = expect float( ent.GetWeaponInfoFileKeyField( "wave_step_dist" ) )
- owner = ent.GetWeaponOwner()
- damageNearValueTitanArmor = weapon.GetWeaponSettingInt( eWeaponVar.damage_near_value_titanarmor )
- }
-
- owner.EndSignal( "OnDestroy" )
-
- for ( int i = 0; i < maxCount; i++ )
- {
- vector newPos = pos + dir * step
-
- vector traceStart = pos
- vector traceEndUnder = newPos
- vector traceEndOver = newPos
-
- if ( !firstTrace )
- {
- traceStart = lastDownPos + <0.0, 0.0, 80.0 >
- traceEndUnder = <newPos.x, newPos.y, traceStart.z - 40.0 >
- traceEndOver = <newPos.x, newPos.y, traceStart.z + step * 0.57735056839> // The over height is to cover the case of a sheer surface that then continues gradually upwards (like mp_box)
- }
- firstTrace = false
-
- VortexBulletHit ornull vortexHit = VortexBulletHitCheck( owner, traceStart, traceEndOver )
- if ( vortexHit )
- {
- expect VortexBulletHit( vortexHit )
- entity vortexWeapon = vortexHit.vortex.GetOwnerWeapon()
-
- if ( vortexWeapon && vortexWeapon.GetWeaponClassName() == "mp_titanweapon_vortex_shield" )
- VortexDrainedByImpact( vortexWeapon, weapon, projectile, null ) // drain the vortex shield
- else if ( IsVortexSphere( vortexHit.vortex ) )
- VortexSphereDrainHealthForDamage( vortexHit.vortex, damageNearValueTitanArmor )
-
- WaitFrame()
- continue
- }
-
- //DebugDrawLine( traceStart, traceEndUnder, 0, 255, 0, true, 25.0 )
- array ignoreArray = []
- if ( IsValid( inflictor ) && inflictor.GetOwner() != null )
- ignoreArray.append( inflictor.GetOwner() )
-
- TraceResults forwardTrace = TraceLine( traceStart, traceEndUnder, ignoreArray, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_BLOCK_WEAPONS )
- if ( forwardTrace.fraction == 1.0 )
- {
- //DebugDrawLine( forwardTrace.endPos, forwardTrace.endPos + <0.0, 0.0, -1000.0>, 255, 0, 0, true, 25.0 )
- TraceResults downTrace = TraceLine( forwardTrace.endPos, forwardTrace.endPos + <0.0, 0.0, -1000.0>, ignoreArray, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_BLOCK_WEAPONS )
- if ( downTrace.fraction == 1.0 )
- break
-
- entity movingGeo = null
- if ( downTrace.hitEnt && downTrace.hitEnt.HasPusherRootParent() && !downTrace.hitEnt.IsMarkedForDeletion() )
- movingGeo = downTrace.hitEnt
-
- if ( !waveFunc( ent, projectileCount, inflictor, movingGeo, downTrace.endPos, angles, i ) )
- return
-
- lastDownPos = downTrace.endPos
- pos = forwardTrace.endPos
-
- WaitFrame()
- continue
- }
- else
- {
- if ( IsValid( forwardTrace.hitEnt ) && (StatusEffect_Get( forwardTrace.hitEnt, eStatusEffect.pass_through_amps_weapon ) > 0) && !CheckPassThroughDir( forwardTrace.hitEnt, forwardTrace.surfaceNormal, forwardTrace.endPos ) )
- break;
- }
-
- TraceResults upwardTrace = TraceLine( traceStart, traceEndOver, ignoreArray, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_BLOCK_WEAPONS )
- //DebugDrawLine( traceStart, traceEndOver, 0, 0, 255, true, 25.0 )
- if ( upwardTrace.fraction < 1.0 )
- {
- if ( IsValid( upwardTrace.hitEnt ) )
- {
- if ( upwardTrace.hitEnt.IsWorld() || upwardTrace.hitEnt.IsPlayer() || upwardTrace.hitEnt.IsNPC() )
- break
- }
- }
- else
- {
- TraceResults downTrace = TraceLine( upwardTrace.endPos, upwardTrace.endPos + <0.0, 0.0, -1000.0>, ignoreArray, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_BLOCK_WEAPONS )
- if ( downTrace.fraction == 1.0 )
- break
-
- entity movingGeo = null
- if ( downTrace.hitEnt && downTrace.hitEnt.HasPusherRootParent() && !downTrace.hitEnt.IsMarkedForDeletion() )
- movingGeo = downTrace.hitEnt
-
- if ( !waveFunc( ent, projectileCount, inflictor, movingGeo, downTrace.endPos, angles, i ) )
- return
-
- lastDownPos = downTrace.endPos
- pos = forwardTrace.endPos
- }
-
- WaitFrame()
- }
-}
-
-void function AddActiveThermiteBurn( entity ent )
-{
- AddToScriptManagedEntArray( file.activeThermiteBurnsManagedEnts, ent )
-}
-
-array<entity> function GetActiveThermiteBurnsWithinRadius( vector origin, float dist, team = TEAM_ANY )
-{
- return GetScriptManagedEntArrayWithinCenter( file.activeThermiteBurnsManagedEnts, team, origin, dist )
-}
-
-void function EMP_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- Elecriticy_DamagedPlayerOrNPC( ent, damageInfo, FX_EMP_BODY_HUMAN, FX_EMP_BODY_TITAN, EMP_SEVERITY_SLOWTURN, EMP_SEVERITY_SLOWMOVE )
-}
-
-void function VanguardEnergySiphon_DamagedPlayerOrNPC( entity ent, var damageInfo )
-{
- entity attacker = DamageInfo_GetAttacker( damageInfo )
- if ( IsValid( attacker ) && attacker.GetTeam() == ent.GetTeam() )
- return
-
- Elecriticy_DamagedPlayerOrNPC( ent, damageInfo, FX_VANGUARD_ENERGY_BODY_HUMAN, FX_VANGUARD_ENERGY_BODY_TITAN, LASER_STUN_SEVERITY_SLOWTURN, LASER_STUN_SEVERITY_SLOWMOVE )
-}
-
-void function Elecriticy_DamagedPlayerOrNPC( entity ent, var damageInfo, asset humanFx, asset titanFx, float slowTurn, float slowMove )
-{
- if ( !IsValid( ent ) )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- local inflictor = DamageInfo_GetInflictor( damageInfo )
- if( !IsValid( inflictor ) )
- return
-
- // Do electrical effect on this ent that everyone can see if they are a titan
- string tag = ""
- asset effect
-
- if ( ent.IsTitan() )
- {
- tag = "exp_torso_front"
- effect = titanFx
- }
- else if ( IsStalker( ent ) || IsSpectre( ent ) )
- {
- tag = "CHESTFOCUS"
- effect = humanFx
- if ( !ent.ContextAction_IsActive() && IsAlive( ent ) && ent.IsInterruptable() )
- {
- ent.Anim_ScriptedPlayActivityByName( "ACT_STUNNED", true, 0.1 )
- }
- }
- else if ( IsSuperSpectre( ent ) )
- {
- tag = "CHESTFOCUS"
- effect = humanFx
-
- if ( ent.GetParent() == null && !ent.ContextAction_IsActive() && IsAlive( ent ) && ent.IsInterruptable() )
- {
- ent.Anim_ScriptedPlayActivityByName( "ACT_STUNNED", true, 0.1 )
- }
- }
- else if ( IsGrunt( ent ) )
- {
- tag = "CHESTFOCUS"
- effect = humanFx
- if ( !ent.ContextAction_IsActive() && IsAlive( ent ) && ent.IsInterruptable() )
- {
- ent.Anim_ScriptedPlayActivityByName( "ACT_STUNNED", true, 0.1 )
- ent.EnableNPCFlag( NPC_PAIN_IN_SCRIPTED_ANIM )
- }
- }
- else if ( IsPilot( ent ) )
- {
- tag = "CHESTFOCUS"
- effect = humanFx
- }
- else if ( IsAirDrone( ent ) )
- {
- if ( GetDroneType( ent ) == "drone_type_marvin" )
- return
- tag = "HEADSHOT"
- effect = humanFx
- thread NpcEmpRebootPrototype( ent, damageInfo, humanFx, titanFx )
- }
- else if ( IsGunship( ent ) )
- {
- tag = "ORIGIN"
- effect = titanFx
- thread NpcEmpRebootPrototype( ent, damageInfo, humanFx, titanFx )
- }
-
- ent.Signal( "ArcStunned" )
-
- if ( tag != "" )
- {
- local inflictor = DamageInfo_GetInflictor( damageInfo )
- Assert( !(inflictor instanceof CEnvExplosion) )
- if ( IsValid( inflictor ) )
- {
- float duration = EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MAX
- if ( inflictor instanceof CBaseGrenade )
- {
- local entCenter = ent.GetWorldSpaceCenter()
- local dist = Distance( DamageInfo_GetDamagePosition( damageInfo ), entCenter )
- local damageRadius = inflictor.GetDamageRadius()
- duration = GraphCapped( dist, damageRadius * 0.5, damageRadius, EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MIN, EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MAX )
- }
- thread EMP_FX( effect, ent, tag, duration )
- }
- }
-
- if ( StatusEffect_Get( ent, eStatusEffect.destroyed_by_emp ) )
- DamageInfo_SetDamage( damageInfo, ent.GetHealth() )
-
- // Don't do arc beams to entities that are on the same team... except the owner
- entity attacker = DamageInfo_GetAttacker( damageInfo )
- if ( IsValid( attacker ) && attacker.GetTeam() == ent.GetTeam() && attacker != ent )
- return
-
- if ( ent.IsPlayer() )
- {
- thread EMPGrenade_EffectsPlayer( ent, damageInfo )
- }
- else if ( ent.IsTitan() )
- {
- EMPGrenade_AffectsShield( ent, damageInfo )
- #if MP
- GiveEMPStunStatusEffects( ent, 2.5, 1.0, slowTurn, slowMove )
- #endif
- thread EMPGrenade_AffectsAccuracy( ent )
- }
- else if ( ent.IsMechanical() )
- {
- #if MP
- GiveEMPStunStatusEffects( ent, 2.5, 1.0, slowTurn, slowMove )
- DamageInfo_ScaleDamage( damageInfo, 2.05 )
- #endif
- }
- else if ( ent.IsHuman() )
- {
- #if MP
- DamageInfo_ScaleDamage( damageInfo, 0.99 )
- #endif
- }
-
- if ( inflictor instanceof CBaseGrenade )
- {
- if ( !ent.IsPlayer() || ent.IsTitan() ) //Beam should hit cloaked targets, when cloak is updated make IsCloaked() function.
- EMPGrenade_ArcBeam( DamageInfo_GetDamagePosition( damageInfo ), ent )
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// HACK: might make sense to move this to code
-void function NpcEmpRebootPrototype( entity npc, var damageInfo, asset humanFx, asset titanFx )
-{
- if ( !IsValid( npc ) )
- return
-
- npc.EndSignal( "OnDeath" )
- npc.EndSignal( "OnDestroy" )
-
- if ( !( "rebooting" in npc.s ) )
- npc.s.rebooting <- null
-
- if ( npc.s.rebooting ) // npc already knocked down and in rebooting process
- return
-
- float rebootTime
- vector groundPos
- local nearestNode
- local neighborNodes
- local groundNodePos
- local origin = npc.GetOrigin()
- local startOrigin = origin
- local classname = npc.GetClassName()
- local soundPowerDown
- local soundPowerUp
-
- //------------------------------------------------------
- // Custom stuff depending on AI type
- //------------------------------------------------------
- switch ( classname )
- {
- case "npc_drone":
- soundPowerDown = "Drone_Power_Down"
- soundPowerUp = "Drone_Power_On"
- rebootTime = DRONE_REBOOT_TIME
- break
- case "npc_gunship":
- soundPowerDown = "Gunship_Power_Down"
- soundPowerUp = "Gunship_Power_On"
- rebootTime = GUNSHIP_REBOOT_TIME
- break
- default:
- Assert( 0, "Unhandled npc type: " + classname )
-
- }
-
- //------------------------------------------------------
- // NPC stunned and is rebooting
- //------------------------------------------------------
- npc.Signal( "OnStunned" )
- npc.s.rebooting = true
-
-
- //TODO: make drone/gunship slowly drift to the ground while rebooting
- /*
- groundPos = OriginToGround( origin )
- groundPos += Vector( 0, 0, 32 )
-
-
- //DebugDrawLine(origin, groundPos, 255, 0, 0, true, 15 )
-
- //thread AssaultOrigin( drone, groundPos, 16 )
- //thread PlayAnim( drone, "idle" )
- */
-
-
- thread EmpRebootFxPrototype( npc, humanFx, titanFx )
- npc.EnableNPCFlag( NPC_IGNORE_ALL )
- npc.SetNoTarget( true )
- npc.EnableNPCFlag( NPC_DISABLE_SENSING ) // don't do traces to look for enemies or players
-
- if ( IsAttackDrone( npc ) )
- npc.SetAttackMode( false )
-
- EmitSoundOnEntity( npc, soundPowerDown )
-
- wait rebootTime
-
- EmitSoundOnEntity( npc, soundPowerUp )
- npc.DisableNPCFlag( NPC_IGNORE_ALL )
- npc.SetNoTarget( false )
- npc.DisableNPCFlag( NPC_DISABLE_SENSING ) // don't do traces to look for enemies or players
-
- if ( IsAttackDrone( npc ) )
- npc.SetAttackMode( true )
-
- npc.s.rebooting = false
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// HACK: might make sense to move this to code
-function EmpRebootFxPrototype( npc, asset humanFx, asset titanFx )
-{
- expect entity( npc )
-
- if ( !IsValid( npc ) )
- return
-
- npc.EndSignal( "OnDeath" )
- npc.EndSignal( "OnDestroy" )
-
- string classname = npc.GetClassName()
- vector origin
- float delayDuration
- entity fxHandle
- asset fxEMPdamage
- string fxTag
- float rebootTime
- string soundEMPdamage
-
- //------------------------------------------------------
- // Custom stuff depending on AI type
- //------------------------------------------------------
- switch ( classname )
- {
- case "npc_drone":
- if ( GetDroneType( npc ) == "drone_type_marvin" )
- return
- fxEMPdamage = humanFx
- fxTag = "HEADSHOT"
- rebootTime = DRONE_REBOOT_TIME
- soundEMPdamage = "Titan_Blue_Electricity_Cloud"
- break
- case "npc_gunship":
- fxEMPdamage = titanFx
- fxTag = "ORIGIN"
- rebootTime = GUNSHIP_REBOOT_TIME
- soundEMPdamage = "Titan_Blue_Electricity_Cloud"
- break
- default:
- Assert( 0, "Unhandled npc type: " + classname )
-
- }
-
- //------------------------------------------------------
- // Play Fx/Sound till reboot finishes
- //------------------------------------------------------
- fxHandle = ClientStylePlayFXOnEntity( fxEMPdamage, npc, fxTag, rebootTime )
- EmitSoundOnEntity( npc, soundEMPdamage )
-
- while ( npc.s.rebooting == true )
- {
- delayDuration = RandomFloatRange( 0.4, 1.2 )
- origin = npc.GetOrigin()
-
-
- EmitSoundAtPosition( npc.GetTeam(), origin, SOUND_EMP_REBOOT_SPARKS )
- PlayFX( FX_EMP_REBOOT_SPARKS, origin )
- PlayFX( FX_EMP_REBOOT_SPARKS, origin )
-
- OnThreadEnd(
- function() : ( fxHandle, npc, soundEMPdamage )
- {
- if ( IsValid( fxHandle ) )
- fxHandle.Fire( "StopPlayEndCap" )
- if ( IsValid( npc ) )
- StopSoundOnEntity( npc, soundEMPdamage )
- }
- )
-
- wait ( delayDuration )
- }
-}
-
-function EMP_FX( asset effect, entity ent, string tag, float duration )
-{
- if ( !IsAlive( ent ) )
- return
-
- ent.Signal( "EMP_FX" )
- ent.EndSignal( "OnDestroy" )
- ent.EndSignal( "OnDeath" )
- ent.EndSignal( "StartPhaseShift" )
- ent.EndSignal( "EMP_FX" )
-
- bool isPlayer = ent.IsPlayer()
-
- int fxId = GetParticleSystemIndex( effect )
- int attachId = ent.LookupAttachment( tag )
-
- entity fxHandle = StartParticleEffectOnEntity_ReturnEntity( ent, fxId, FX_PATTACH_POINT_FOLLOW, attachId )
- fxHandle.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY
- fxHandle.SetOwner( ent )
-
- OnThreadEnd(
- function() : ( fxHandle, ent )
- {
- if ( IsValid( fxHandle ) )
- {
- EffectStop( fxHandle )
- }
-
- if ( IsValid( ent ) )
- StopSoundOnEntity( ent, "Titan_Blue_Electricity_Cloud" )
- }
- )
-
- if ( !isPlayer )
- {
- EmitSoundOnEntity( ent, "Titan_Blue_Electricity_Cloud" )
- wait duration
- }
- else
- {
- EmitSoundOnEntityExceptToPlayer( ent, ent, "Titan_Blue_Electricity_Cloud" )
-
- var endTime = Time() + duration
- bool effectsActive = true
- while( endTime > Time() )
- {
- if ( ent.IsPhaseShifted() )
- {
- if ( effectsActive )
- {
- effectsActive = false
- if ( IsValid( fxHandle ) )
- EffectSleep( fxHandle )
-
- if ( IsValid( ent ) )
- StopSoundOnEntity( ent, "Titan_Blue_Electricity_Cloud" )
- }
- }
- else if ( effectsActive == false )
- {
- EffectWake( fxHandle )
- EmitSoundOnEntityExceptToPlayer( ent, ent, "Titan_Blue_Electricity_Cloud" )
- effectsActive = true
- }
-
- WaitFrame()
- }
- }
-}
-
-function EMPGrenade_AffectsShield( entity titan, damageInfo )
-{
- int shieldHealth = titan.GetTitanSoul().GetShieldHealth()
- int shieldDamage = int( titan.GetTitanSoul().GetShieldHealthMax() * 0.5 )
-
- titan.GetTitanSoul().SetShieldHealth( maxint( 0, shieldHealth - shieldDamage ) )
-
- // attacker took down titan shields
- if ( shieldHealth && !titan.GetTitanSoul().GetShieldHealth() )
- {
- entity attacker = DamageInfo_GetAttacker( damageInfo )
- if ( attacker && attacker.IsPlayer() )
- EmitSoundOnEntityOnlyToPlayer( attacker, attacker, "titan_energyshield_down" )
- }
-}
-
-function EMPGrenade_AffectsAccuracy( npcTitan )
-{
- npcTitan.EndSignal( "OnDestroy" )
-
- npcTitan.kv.AccuracyMultiplier = 0.5
- wait EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MAX
- npcTitan.kv.AccuracyMultiplier = 1.0
-}
-
-
-function EMPGrenade_EffectsPlayer( entity player, damageInfo )
-{
- player.Signal( "OnEMPPilotHit" )
- player.EndSignal( "OnEMPPilotHit" )
-
- if ( player.IsPhaseShifted() )
- return
-
- entity inflictor = DamageInfo_GetInflictor( damageInfo )
- local dist = Distance( DamageInfo_GetDamagePosition( damageInfo ), player.GetWorldSpaceCenter() )
- local damageRadius = 128
- if ( inflictor instanceof CBaseGrenade )
- damageRadius = inflictor.GetDamageRadius()
- float frac = GraphCapped( dist, damageRadius * 0.5, damageRadius, 1.0, 0.0 )
- local strength = EMP_GRENADE_PILOT_SCREEN_EFFECTS_MIN + ( ( EMP_GRENADE_PILOT_SCREEN_EFFECTS_MAX - EMP_GRENADE_PILOT_SCREEN_EFFECTS_MIN ) * frac )
- float fadeoutDuration = EMP_GRENADE_PILOT_SCREEN_EFFECTS_FADE * frac
- float duration = EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MIN + ( ( EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MAX - EMP_GRENADE_PILOT_SCREEN_EFFECTS_DURATION_MIN ) * frac ) - fadeoutDuration
- local origin = inflictor.GetOrigin()
-
- int dmgSource = DamageInfo_GetDamageSourceIdentifier( damageInfo )
- if ( dmgSource == eDamageSourceId.mp_weapon_proximity_mine || dmgSource == eDamageSourceId.mp_titanweapon_stun_laser )
- {
- strength *= 0.1
- }
-
- if ( player.IsTitan() )
- {
- // Hit player should do EMP screen effects locally
- Remote_CallFunction_Replay( player, "ServerCallback_TitanCockpitEMP", duration )
-
- EMPGrenade_AffectsShield( player, damageInfo )
-
- Remote_CallFunction_Replay( player, "ServerCallback_TitanEMP", strength, duration, fadeoutDuration )
- }
- else
- {
- if ( IsCloaked( player ) )
- player.SetCloakFlicker( 0.5, duration )
-
- // duration = 0
- // fadeoutDuration = 0
-
- StatusEffect_AddTimed( player, eStatusEffect.emp, strength, duration, fadeoutDuration )
- //DamageInfo_SetDamage( damageInfo, 0 )
- }
-
- GiveEMPStunStatusEffects( player, (duration + fadeoutDuration), fadeoutDuration)
-}
-
-function EMPGrenade_ArcBeam( grenadePos, ent )
-{
- if ( !ent.IsPlayer() && !ent.IsNPC() )
- return
-
- Assert( IsValid( ent ) )
- local lifeDuration = 0.5
-
- // Control point sets the end position of the effect
- entity cpEnd = CreateEntity( "info_placement_helper" )
- SetTargetName( cpEnd, UniqueString( "emp_grenade_beam_cpEnd" ) )
- cpEnd.SetOrigin( grenadePos )
- DispatchSpawn( cpEnd )
-
- entity zapBeam = CreateEntity( "info_particle_system" )
- zapBeam.kv.cpoint1 = cpEnd.GetTargetName()
- zapBeam.SetValueForEffectNameKey( EMP_GRENADE_BEAM_EFFECT )
- zapBeam.kv.start_active = 0
- zapBeam.SetOrigin( ent.GetWorldSpaceCenter() )
- if ( !ent.IsMarkedForDeletion() ) // TODO: This is a hack for shipping. Should not be parenting to deleted entities
- {
- zapBeam.SetParent( ent, "", true, 0.0 )
- }
-
- DispatchSpawn( zapBeam )
-
- zapBeam.Fire( "Start" )
- zapBeam.Fire( "StopPlayEndCap", "", lifeDuration )
- zapBeam.Kill_Deprecated_UseDestroyInstead( lifeDuration )
- cpEnd.Kill_Deprecated_UseDestroyInstead( lifeDuration )
-}
-
-void function GetWeaponDPS( bool vsTitan = false )
-{
- entity player = GetPlayerArray()[0]
- entity weapon = player.GetActiveWeapon()
-
- local fire_rate = weapon.GetWeaponInfoFileKeyField( "fire_rate" )
- local burst_fire_count = weapon.GetWeaponInfoFileKeyField( "burst_fire_count" )
- local burst_fire_delay = weapon.GetWeaponInfoFileKeyField( "burst_fire_delay" )
-
- local damage_near_value = weapon.GetWeaponInfoFileKeyField( "damage_near_value" )
- local damage_far_value = weapon.GetWeaponInfoFileKeyField( "damage_far_value" )
-
- if ( vsTitan )
- {
- damage_near_value = weapon.GetWeaponInfoFileKeyField( "damage_near_value_titanarmor" )
- damage_far_value = weapon.GetWeaponInfoFileKeyField( "damage_far_value_titanarmor" )
- }
-
- if ( burst_fire_count )
- {
- local timePerShot = 1 / fire_rate
- local timePerBurst = (timePerShot * burst_fire_count) + burst_fire_delay
- local burstPerSecond = 1 / timePerBurst
-
- printt( timePerBurst )
-
- printt( "DPS Near", (burstPerSecond * burst_fire_count) * damage_near_value )
- printt( "DPS Far ", (burstPerSecond * burst_fire_count) * damage_far_value )
- }
- else
- {
- printt( "DPS Near", fire_rate * damage_near_value )
- printt( "DPS Far ", fire_rate * damage_far_value )
- }
-}
-
-
-void function GetTTK( string weaponRef, float health = 100.0 )
-{
- local fire_rate = GetWeaponInfoFileKeyField_Global( weaponRef, "fire_rate" ).tofloat()
- local burst_fire_count = GetWeaponInfoFileKeyField_Global( weaponRef, "burst_fire_count" )
- if ( burst_fire_count != null )
- burst_fire_count = burst_fire_count.tofloat()
-
- local burst_fire_delay = GetWeaponInfoFileKeyField_Global( weaponRef, "burst_fire_delay" )
- if ( burst_fire_delay != null )
- burst_fire_delay = burst_fire_delay.tofloat()
-
- local damage_near_value = GetWeaponInfoFileKeyField_Global( weaponRef, "damage_near_value" ).tointeger()
- local damage_far_value = GetWeaponInfoFileKeyField_Global( weaponRef, "damage_far_value" ).tointeger()
-
- local nearBodyShots = ceil( health / damage_near_value ) - 1
- local farBodyShots = ceil( health / damage_far_value ) - 1
-
- local delayAdd = 0
- if ( burst_fire_count && burst_fire_count < nearBodyShots )
- delayAdd += burst_fire_delay
-
- printt( "TTK Near", (nearBodyShots * (1 / fire_rate)) + delayAdd, " (" + (nearBodyShots + 1) + ")" )
-
-
- delayAdd = 0
- if ( burst_fire_count && burst_fire_count < farBodyShots )
- delayAdd += burst_fire_delay
-
- printt( "TTK Far ", (farBodyShots * (1 / fire_rate)) + delayAdd, " (" + (farBodyShots + 1) + ")" )
-}
-
-array<string> function GetWeaponModsFromDamageInfo( var damageInfo )
-{
- entity weapon = DamageInfo_GetWeapon( damageInfo )
- entity inflictor = DamageInfo_GetInflictor( damageInfo )
- int damageType = DamageInfo_GetCustomDamageType( damageInfo )
-
- if ( IsValid( weapon ) )
- {
- return weapon.GetMods()
- }
- else if ( IsValid( inflictor ) )
- {
- if ( "weaponMods" in inflictor.s && inflictor.s.weaponMods )
- {
- array<string> temp
- foreach ( string mod in inflictor.s.weaponMods )
- {
- temp.append( mod )
- }
-
- return temp
- }
- else if( inflictor.IsProjectile() )
- return inflictor.ProjectileGetMods()
- else if ( damageType & DF_EXPLOSION && inflictor.IsPlayer() && IsValid( inflictor.GetActiveWeapon() ) )
- return inflictor.GetActiveWeapon().GetMods()
- //Hack - Splash damage doesn't pass mod weapon through. This only works under the assumption that offhand weapons don't have mods.
- }
- return []
-}
-
-void function OnPlayerGetsNewPilotLoadout( entity player, PilotLoadoutDef loadout )
-{
- if ( GetCurrentPlaylistVarInt( "featured_mode_amped_tacticals", 0 ) >= 1 )
- {
- player.GiveExtraWeaponMod( "amped_tacticals" )
- }
-
- if ( GetCurrentPlaylistVarInt( "featured_mode_all_grapple", 0 ) >= 1 )
- {
- player.GiveExtraWeaponMod( "all_grapple" )
- }
-
- if ( GetCurrentPlaylistVarInt( "featured_mode_all_phase", 0 ) >= 1 )
- {
- player.GiveExtraWeaponMod( "all_phase" )
- }
-
- SetPlayerCooldowns( player )
-}
-
-void function SetPlayerCooldowns( entity player )
-{
- if ( player.IsTitan() )
- return
-
- array<int> offhandIndices = [ OFFHAND_LEFT, OFFHAND_RIGHT ]
-
- foreach ( index in offhandIndices )
- {
- float lastUseTime = player.p.lastPilotOffhandUseTime[ index ]
- float lastChargeFrac = player.p.lastPilotOffhandChargeFrac[ index ]
- float lastClipFrac = player.p.lastPilotClipFrac[ index ]
-
- if ( lastUseTime >= 0.0 )
- {
- entity weapon = player.GetOffhandWeapon( index )
- if ( !IsValid( weapon ) )
- continue
-
- string weaponClassName = weapon.GetWeaponClassName()
-
- switch ( GetWeaponInfoFileKeyField_Global( weaponClassName, "cooldown_type" ) )
- {
- case "grapple":
- // GetPlayerSettingsField isn't working for moddable fields? - Bug 129567
- float powerRequired = 100.0 // GetPlayerSettingsField( "grapple_power_required" )
- float regenRefillDelay = 3.0 // GetPlayerSettingsField( "grapple_power_regen_delay" )
- float regenRefillRate = 5.0 // GetPlayerSettingsField( "grapple_power_regen_rate" )
- float suitPowerToRestore = powerRequired - player.p.lastSuitPower
- float regenRefillTime = suitPowerToRestore / regenRefillRate
-
- float regenStartTime = lastUseTime + regenRefillDelay
-
- float newSuitPower = GraphCapped( Time() - regenStartTime, 0.0, regenRefillTime, player.p.lastSuitPower, powerRequired )
-
- player.SetSuitGrapplePower( newSuitPower )
- break
-
- case "ammo":
- case "ammo_instant":
- case "ammo_deployed":
- case "ammo_timed":
- int maxAmmo = weapon.GetWeaponPrimaryClipCountMax()
- float fireDuration = weapon.GetWeaponSettingFloat( eWeaponVar.fire_duration )
- float regenRefillDelay = weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_start_delay )
- float regenRefillRate = weapon.GetWeaponSettingFloat( eWeaponVar.regen_ammo_refill_rate )
- int startingClipCount = int( lastClipFrac * maxAmmo )
- int ammoToRestore = maxAmmo - startingClipCount
- float regenRefillTime = ammoToRestore / regenRefillRate
-
- float regenStartTime = lastUseTime + fireDuration + regenRefillDelay
-
- int newAmmo = int( GraphCapped( Time() - regenStartTime, 0.0, regenRefillTime, startingClipCount, maxAmmo ) )
-
- weapon.SetWeaponPrimaryClipCountAbsolute( newAmmo )
- break
-
- case "chargeFrac":
- float chargeCooldownDelay = weapon.GetWeaponSettingFloat( eWeaponVar.charge_cooldown_delay )
- float chargeCooldownTime = weapon.GetWeaponSettingFloat( eWeaponVar.charge_cooldown_time )
- float regenRefillTime = lastChargeFrac * chargeCooldownTime
- float regenStartTime = lastUseTime + chargeCooldownDelay
-
- float newCharge = GraphCapped( Time() - regenStartTime, 0.0, regenRefillTime, lastChargeFrac, 0.0 )
-
- weapon.SetWeaponChargeFraction( newCharge )
- break
-
- default:
- printt( weaponClassName + " needs to be updated to support cooldown_type setting" )
- break
- }
- }
- }
-}
-
-void function ResetPlayerCooldowns( entity player )
-{
- if ( player.IsTitan() )
- return
-
- array<int> offhandIndices = [ OFFHAND_LEFT, OFFHAND_RIGHT ]
-
- foreach ( index in offhandIndices )
- {
- float lastUseTime = -99.0//player.p.lastPilotOffhandUseTime[ index ]
- float lastChargeFrac = -1.0//player.p.lastPilotOffhandChargeFrac[ index ]
- float lastClipFrac = 1.0//player.p.lastPilotClipFrac[ index ]
-
- entity weapon = player.GetOffhandWeapon( index )
- if ( !IsValid( weapon ) )
- continue
-
- string weaponClassName = weapon.GetWeaponClassName()
-
- switch ( GetWeaponInfoFileKeyField_Global( weaponClassName, "cooldown_type" ) )
- {
- case "grapple":
- // GetPlayerSettingsField isn't working for moddable fields? - Bug 129567
- float powerRequired = 100.0 // GetPlayerSettingsField( "grapple_power_required" )
- player.SetSuitGrapplePower( powerRequired )
- break
-
- case "ammo":
- case "ammo_instant":
- case "ammo_deployed":
- case "ammo_timed":
- int maxAmmo = weapon.GetWeaponPrimaryClipCountMax()
- weapon.SetWeaponPrimaryClipCountAbsolute( maxAmmo )
- break
-
- case "chargeFrac":
- weapon.SetWeaponChargeFraction( 1.0 )
- break
-
- default:
- printt( weaponClassName + " needs to be updated to support cooldown_type setting" )
- break
- }
- }
-}
-
-void function OnPlayerKilled( entity player, entity attacker, var damageInfo )
-{
- StoreOffhandData( player )
-}
-
-void function StoreOffhandData( entity player, bool waitEndFrame = true )
-{
- thread StoreOffhandDataThread( player, waitEndFrame )
-}
-
-void function StoreOffhandDataThread( entity player, bool waitEndFrame )
-{
- if ( !IsValid( player ) )
- return
-
- player.EndSignal( "OnDestroy" )
-
- if ( waitEndFrame )
- WaitEndFrame() // Need to WaitEndFrame so clip counts can be updated if player is dying the same frame
-
- array<int> offhandIndices = [ OFFHAND_LEFT, OFFHAND_RIGHT ]
-
- // Reset all values for full cooldown
- player.p.lastSuitPower = 0.0
-
- foreach ( index in offhandIndices )
- {
- player.p.lastPilotOffhandChargeFrac[ index ] = 1.0
- player.p.lastPilotClipFrac[ index ] = 1.0
-
- player.p.lastTitanOffhandChargeFrac[ index ] = 1.0
- player.p.lastTitanClipFrac[ index ] = 1.0
- }
-
- if ( player.IsTitan() )
- return
-
- foreach ( index in offhandIndices )
- {
- entity weapon = player.GetOffhandWeapon( index )
- if ( !IsValid( weapon ) )
- continue
-
- string weaponClassName = weapon.GetWeaponClassName()
-
- switch ( GetWeaponInfoFileKeyField_Global( weaponClassName, "cooldown_type" ) )
- {
- case "grapple":
- player.p.lastSuitPower = player.GetSuitGrapplePower()
- break
-
- case "ammo":
- case "ammo_instant":
- case "ammo_deployed":
- case "ammo_timed":
-
- if ( player.IsTitan() )
- {
- if ( !weapon.IsWeaponRegenDraining() )
- player.p.lastTitanClipFrac[ index ] = min( 1.0, weapon.GetWeaponPrimaryClipCount() / float( weapon.GetWeaponPrimaryClipCountMax() ) ) //Was returning greater than one with extraweaponmod timing.
- else
- player.p.lastTitanClipFrac[ index ] = 0.0
- }
- else
- {
- if ( !weapon.IsWeaponRegenDraining() )
- player.p.lastPilotClipFrac[ index ] = min( 1.0, weapon.GetWeaponPrimaryClipCount() / float( weapon.GetWeaponPrimaryClipCountMax() ) ) //Was returning greater than one with extraweaponmod timing.
- else
- player.p.lastPilotClipFrac[ index ] = 0.0
- }
- break
-
- case "chargeFrac":
- if ( player.IsTitan() )
- player.p.lastTitanOffhandChargeFrac[ index ] = weapon.GetWeaponChargeFraction()
- else
- player.p.lastPilotOffhandChargeFrac[ index ] = weapon.GetWeaponChargeFraction()
- break
-
- default:
- printt( weaponClassName + " needs to be updated to support cooldown_type setting" )
- break
- }
- }
-}
-#endif // #if SERVER
-
-void function PlayerUsedOffhand( entity player, entity offhandWeapon )
-{
- array<int> offhandIndices = [ OFFHAND_LEFT, OFFHAND_RIGHT, OFFHAND_ANTIRODEO, OFFHAND_EQUIPMENT ]
-
- foreach ( index in offhandIndices )
- {
- entity weapon = player.GetOffhandWeapon( index )
- if ( !IsValid( weapon ) )
- continue
-
- if ( weapon != offhandWeapon )
- continue
-
- #if SERVER
- if ( player.IsTitan() )
- player.p.lastTitanOffhandUseTime[ index ] = Time()
- else
- player.p.lastPilotOffhandUseTime[ index ] = Time()
-
- #if MP
- string weaponName = offhandWeapon.GetWeaponClassName()
- if ( weaponName != "mp_ability_grapple" ) // handled in CodeCallback_OnGrapple // nope, it's not (?)
- {
- string category
- float duration
- if ( index == OFFHAND_EQUIPMENT && player.IsTitan() )
- {
- category = "core"
- duration = -1
- }
- else
- {
- category = ""
- duration = Time() - offhandWeapon.GetNextAttackAllowedTimeRaw()
- }
- PIN_PlayerAbility( player, category, weaponName, {}, duration )
- }
- #endif
- #endif // SERVER
-
- #if HAS_TITAN_TELEMETRY && CLIENT
- ClTitanHints_ClearOffhandHint( index )
- #endif
-
- #if HAS_TITAN_TELEMETRY && SERVER
- TitanHints_NotifyUsedOffhand( index )
- #endif
-
- return
- }
-}
-
-RadiusDamageData function GetRadiusDamageDataFromProjectile( entity projectile, entity owner )
-{
- RadiusDamageData radiusDamageData
-
- radiusDamageData.explosionDamage = -1
- radiusDamageData.explosionDamageHeavyArmor = -1
-
- if ( owner.IsNPC() )
- {
- radiusDamageData.explosionDamage = projectile.GetProjectileWeaponSettingInt( eWeaponVar.npc_explosion_damage )
- radiusDamageData.explosionDamageHeavyArmor = projectile.GetProjectileWeaponSettingInt( eWeaponVar.npc_explosion_damage_heavy_armor )
- }
-
- if ( radiusDamageData.explosionDamage == -1 )
- radiusDamageData.explosionDamage = projectile.GetProjectileWeaponSettingInt( eWeaponVar.explosion_damage )
-
- if ( radiusDamageData.explosionDamageHeavyArmor == -1 )
- radiusDamageData.explosionDamageHeavyArmor = projectile.GetProjectileWeaponSettingInt( eWeaponVar.explosion_damage_heavy_armor )
-
- radiusDamageData.explosionRadius = projectile.GetProjectileWeaponSettingFloat( eWeaponVar.explosionradius )
- radiusDamageData.explosionInnerRadius = projectile.GetProjectileWeaponSettingFloat( eWeaponVar.explosion_inner_radius )
-
- Assert( radiusDamageData.explosionRadius > 0, "Created RadiusDamageData with 0 radius" )
- Assert( radiusDamageData.explosionDamage > 0 || radiusDamageData.explosionDamageHeavyArmor > 0, "Created RadiusDamageData with 0 damage" )
- return radiusDamageData
-}
-
-#if SERVER
-void function Thermite_DamagePlayerOrNPCSounds( entity ent )
-{
- if ( ent.IsTitan() )
- {
- if ( ent.IsPlayer() )
- {
- EmitSoundOnEntityOnlyToPlayer( ent, ent, "titan_thermiteburn_3p_vs_1p" )
- EmitSoundOnEntityExceptToPlayer( ent, ent, "titan_thermiteburn_1p_vs_3p" )
- }
- else
- {
- EmitSoundOnEntity( ent, "titan_thermiteburn_1p_vs_3p" )
- }
- }
- else
- {
- if ( ent.IsPlayer() )
- {
- EmitSoundOnEntityOnlyToPlayer( ent, ent, "flesh_thermiteburn_3p_vs_1p" )
- EmitSoundOnEntityExceptToPlayer( ent, ent, "flesh_thermiteburn_1p_vs_3p" )
- }
- else
- {
- EmitSoundOnEntity( ent, "flesh_thermiteburn_1p_vs_3p" )
- }
- }
-}
-#endif
-
-#if SERVER
-void function RemoveThreatScopeColorStatusEffect( entity player )
-{
- for ( int i = file.colorSwapStatusEffects.len() - 1; i >= 0; i-- )
- {
- entity owner = file.colorSwapStatusEffects[i].weaponOwner
- if ( !IsValid( owner ) )
- {
- file.colorSwapStatusEffects.remove( i )
- continue
- }
- if ( owner == player )
- {
- StatusEffect_Stop( player, file.colorSwapStatusEffects[i].statusEffectId )
- file.colorSwapStatusEffects.remove( i )
- }
- }
-}
-
-void function AddThreatScopeColorStatusEffect( entity player )
-{
- ColorSwapStruct info
- info.weaponOwner = player
- info.statusEffectId = StatusEffect_AddTimed( player, eStatusEffect.cockpitColor, COCKPIT_COLOR_THREAT, 100000, 0 )
- file.colorSwapStatusEffects.append( info )
-}
-#endif