From 9a96d0bff56f1969c68bb52a2f33296095bdc67d Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Tue, 31 Aug 2021 23:14:58 +0100 Subject: move to new mod format --- .../scripts/vscripts/weapons/_arc_cannon.nut | 1032 -------------------- 1 file changed, 1032 deletions(-) delete mode 100644 Northstar.CustomServers/scripts/vscripts/weapons/_arc_cannon.nut (limited to 'Northstar.CustomServers/scripts/vscripts/weapons/_arc_cannon.nut') 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 1601330c8..000000000 --- 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 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 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 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 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 function GetArcCannonChainTargets( vector fromOrigin, entity fromTarget, table zapInfo ) -{ - // NOTE: fromTarget could be null/invalid if it was a drone - array results = [] - if ( !IsValid( zapInfo.player ) ) - return results - - int playerTeam = expect entity( zapInfo.player ).GetTeam() - array 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 function GetArcCannonTargets( vector origin, int team ) -{ - array 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 function GetArcCannonTargetsInRange( vector origin, int team, entity weapon ) -{ - array allTargets = GetArcCannonTargets( origin, team ) - array 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 -- cgit v1.2.3