og-r1delta-core/scripts/vscripts/_codecallbacks_mp.nut
2026-07-24 16:40:50 -04:00

590 lines
No EOL
16 KiB
Text

const VERBOSE_DAMAGE_PRINTOUTS = false
// TODO: Get an equivalent callback happening on the client, so we can stop using ServerCallback_PlayerTookDamage which is always out of date to some degree.
function CodeCallback_DamagePlayerOrNPC( ent, damageInfo )
{
if ( VERBOSE_DAMAGE_PRINTOUTS )
{
printt( "CodeCallback_DamagePlayerOrNPC ent:", ent )
printt( " Original damage:", damageInfo.GetDamage() )
}
local entIsPlayer = ent.IsPlayer()
local entIsTitan = ent.IsTitan()
if ( !ScriptCallback_ShouldEntTakeDamage( ent, damageInfo ) )
{
// EMP triggers on damage, but in some cases players are invlunerable (embark, disembark, etc...)
if ( entIsPlayer && damageInfo.GetDamageSourceIdentifier() in level._empForcedCallbacks )
{
local attacker = damageInfo.GetAttacker()
if ( ShouldPlayEMPEffectEvenWhenDamageIsZero( ent, attacker ) )
EMPGrenade_DamagedPlayerOrNPC( ent, damageInfo )
}
damageInfo.SetDamage( 0 )
return
}
local attacker = damageInfo.GetAttacker()
local inflictor = damageInfo.GetInflictor()
if ( damageInfo.GetDamageSourceIdentifier() == eDamageSourceId.titan_step )
HandleFootstepDamage( ent, damageInfo )
// HACK helps trap/grenade weapons do damage to the correct entities (player who deployed it as well as the team opposite his)
if ( IsValid( inflictor ) && "originalOwner" in inflictor.s )
{
local ogOwner = inflictor.s.originalOwner
if ( IsValid( ogOwner ) )
{
// if the victim is the guy who damaged the trap, and he is not the ogOwner...
if ( ent == attacker && ent != ogOwner )
{
// HACK to do this legit we need damageInfo.SetAttacker()
// victim should take damage from the original owner instead of the satchel attacker so he gets a kill credit
ent.TakeDamage( damageInfo.GetDamage(), ogOwner, inflictor, { weapon = damageInfo.GetWeapon(), origin = damageInfo.GetDamagePosition(), force = damageInfo.GetDamageForce(), scriptType = damageInfo.GetCustomDamageType(), damageSourceId = damageInfo.GetDamageSourceIdentifier() } )
// now zero out the normal damage and return
damageInfo.SetDamage( 0 )
return
}
}
}
if ( IsValid( inflictor ) && inflictor.IsProjectile() && entIsPlayer )
{
// Don't take damage from projectiles created before you where spawned.
if ( inflictor.GetProjectileCreationTime() < ent.s.respawnTime && ( Time() - ent.s.respawnTime ) < 2.0 )
{
damageInfo.SetDamage( 0 )
return
}
}
// Round damage to nearest full value
damageInfo.SetDamage( floor( damageInfo.GetDamage() + 0.5 ) )
if ( damageInfo.GetDamage() == 0 )
return
if ( VERBOSE_DAMAGE_PRINTOUTS )
printt( " rounded damage amount:", damageInfo.GetDamage() )
HandleLocationBasedDamage( ent, damageInfo )
if ( VERBOSE_DAMAGE_PRINTOUTS )
printt( " after location based damage:", damageInfo.GetDamage() )
// Set damage source correctly when npc grunts or titans try to melee us
if ( attacker.IsNPC() && damageInfo.GetDamageType() == DMG_CLUB )
{
if ( IsValid( attacker ) )
{
if ( attacker.IsTitan() )
damageInfo.SetDamageSourceIdentifier( eDamageSourceId.titan_melee )
else if ( attacker.IsSpectre() )
damageInfo.SetDamageSourceIdentifier( eDamageSourceId.spectre_melee )
else
damageInfo.SetDamageSourceIdentifier( eDamageSourceId.grunt_melee )
}
}
// use AddDamageCallback( "classname", function ) to registed functions
local className = ent.GetClassname()
if ( className in level.damageCallbacks )
{
foreach ( callbackInfo in level.damageCallbacks[className] )
{
callbackInfo.func.acall( [callbackInfo.scope, ent, damageInfo] )
if ( damageInfo.GetDamage() == 0 )
return
}
}
// use AddDamageByCallback( "classname", function ) to registed functions
if ( IsValid( attacker ) )
{
if ( attacker.IsTitan() )
{
local soul = attacker.GetTitanSoul()
if ( soul in level.titanDamageScaler )
damageInfo.SetDamage( damageInfo.GetDamage() * level.titanDamageScaler[ soul ] )
}
local attackerClassName = attacker.GetClassname()
if ( attackerClassName in level.damageByCallbacks )
{
foreach ( callbackInfo in level.damageByCallbacks[attackerClassName] )
{
callbackInfo.func.acall( [callbackInfo.scope, ent, damageInfo] )
if ( damageInfo.GetDamage() == 0 )
return
}
}
}
if ( VERBOSE_DAMAGE_PRINTOUTS )
printt( " after damage callbacks:", damageInfo.GetDamage() )
// use AddDamageCallbackSourceID( "classname", function ) to registed functions
local damageSourceId = damageInfo.GetDamageSourceIdentifier()
if ( damageSourceId in level.damageCallbacksSourceID )
{
foreach ( callbackInfo in level.damageCallbacksSourceID[damageSourceId] )
{
callbackInfo.func.acall( [callbackInfo.scope, ent, damageInfo] )
if ( damageInfo.GetDamage() == 0 )
break
}
}
if ( damageInfo.GetDamage() == 0 )
return
if ( VERBOSE_DAMAGE_PRINTOUTS )
printt( " after damageSourceID callbacks:", damageInfo.GetDamage() )
if ( damageInfo.GetCustomDamageType() & DF_DOOMED_HEALTH_LOSS )
damageInfo.AddDamageFlags( DAMAGEFLAG_NOPAIN )
local savedDamage = damageInfo.GetDamage()
local shieldDamage = 0
if ( entIsPlayer )
{
shieldDamage = PlayerTookDamage( ent, damageInfo, attacker, inflictor, damageSourceId )
if ( damageInfo.GetDamage() == 0 )
return
PlayerDamageFeedback( ent, damageInfo )
savedDamage = damageInfo.GetDamage()
if ( !entIsTitan )
ent.SetCloakFlicker( 0.5, 0.65 )
}
else
{
if ( entIsTitan )
{
shieldDamage = Titan_NPCTookDamage( ent, damageInfo )
savedDamage = damageInfo.GetDamage()
}
PlayerDamageFeedback( ent, damageInfo )
}
if ( VERBOSE_DAMAGE_PRINTOUTS )
printt( " After player damage mod:", damageInfo.GetDamage() )
UpdateLastDamageTime( ent )
ReportDevStat_TimeTillEngagement( attacker, ent, damageInfo )
//pain sounds _base_gametype.nut, death sounds in _death_package.nut
UpdateDamageState( ent, damageInfo )
HandlePainSounds( ent, damageInfo )
UpdateAttackerInfo( ent, attacker, savedDamage )
if ( attacker.IsPlayer() && !(damageInfo.GetCustomDamageType() & DF_DOOMED_HEALTH_LOSS) )
{
if ( entIsTitan )
{
local entSoul = ent.GetTitanSoul()
if ( attacker.currentTargetPlayerOrSoul_Ent != entSoul )
{
attacker.currentTargetPlayerOrSoul_Ent = ent.GetTitanSoul()
local enemyTeam = GetOtherTeam( ent.GetTeam() )
TitanVO_TellPlayersThatAreAlsoFightingThisTarget( attacker, entSoul, enemyTeam )
}
attacker.currentTargetPlayerOrSoul_LastHitTime = Time()
}
else if ( entIsPlayer )
{
attacker.currentTargetPlayerOrSoul_Ent = ent
attacker.currentTargetPlayerOrSoul_LastHitTime = Time()
}
local titanSpawnDelay = GetTitanBuildTime( attacker )
local timerCredit = 0
if ( ShouldGiveTimerCredit( attacker, ent ) )
{
if ( titanSpawnDelay && IsAlive( ent ) )
{
if ( entIsTitan )
{
timerCredit = GetCurrentPlaylistVarFloat( "titan_kill_credit", 0.5 )
if ( PlayerHasServerFlag( attacker, SFLAG_HUNTER_TITAN ) )
timerCredit *= 2.0
}
else
{
if ( entIsPlayer )
{
timerCredit = GetCurrentPlaylistVarFloat( "player_kill_credit", 0.5 )
if ( PlayerHasServerFlag( attacker, SFLAG_HUNTER_PILOT ) )
timerCredit *= 2.5
}
else
{
if ( ent.IsSoldier() )
{
timerCredit = GetCurrentPlaylistVarFloat( "ai_kill_credit", 0.5 )
if ( PlayerHasServerFlag( attacker, SFLAG_HUNTER_GRUNT ) )
timerCredit *= 2.5
}
else
if ( ent.IsSpectre() )
{
timerCredit = GetCurrentPlaylistVarFloat( "spectre_kill_credit", 0.5 )
if ( PlayerHasServerFlag( attacker, SFLAG_HUNTER_SPECTRE ) )
timerCredit *= 2.5
}
else
if ( ent.IsTurret() )
{
timerCredit = GetCurrentPlaylistVarFloat( "megaturret_kill_credit", 0.5 )
//No 2x burn card for shooting mega turret
}
else
if ( IsEvacDropship( ent ) )
{
timerCredit = GetCurrentPlaylistVarFloat( "evac_dropship_kill_credit", 0.5 )
}
}
}
if ( attacker.IsTitan() && PlayerHasPassive( attacker, PAS_HYPER_CORE ) )
timerCredit *= 2.0
local dealtDamage = min( ent.GetHealth(), savedDamage + shieldDamage )
timerCredit = timerCredit * (dealtDamage / ent.GetMaxHealth().tofloat())
}
if ( timerCredit )
DecrementBuildTimer( attacker, timerCredit )
}
}
if ( entIsTitan )
{
TitanDamageFlinch( ent, attacker, damageInfo )
}
if ( VERBOSE_DAMAGE_PRINTOUTS )
printt( " final damage done:", damageInfo.GetDamage() )
}
function TitanDamageFlinch( ent, attacker, damageInfo )
{
if ( damageInfo.GetCustomDamageType() & DF_DOOMED_HEALTH_LOSS )
return
if ( damageInfo.GetCustomDamageType() & DF_NO_FLINCH )
return
if ( TitanStagger( ent, damageInfo ) )
return
if ( damageInfo.GetDamage() >= TITAN_ADDITIVE_FLINCH_DAMAGE_THRESHOLD )
{
AddFlinch( ent, damageInfo )
}
}
function GetDamageOrigin( damageInfo, victim = null )
{
local damageSourceId = damageInfo.GetDamageSourceIdentifier()
local inflictor = damageInfo.GetInflictor()
if ( inflictor == level.worldspawn )
return damageInfo.GetDamagePosition()
local damageOrigin = IsValid( inflictor ) ? inflictor.GetOrigin() : damageInfo.GetDamagePosition()
switch ( damageSourceId )
{
case eDamageSourceId.mp_weapon_laser_mine:
case eDamageSourceId.mp_weapon_satchel:
case eDamageSourceId.mp_weapon_proximity_mine:
break
case eDamageSourceId.nuclear_core:
case eDamageSourceId.mp_titanability_smoke:
//if ( IsValid( victim ) && victim.IsPlayer() && IsValid( victim.GetTitanSoulBeingRodeoed() ) )
{
damageOrigin += (RandomVecInDome( Vector( 0, 0, -1 ) ) * SMOKESCREEN_DAMAGE_OUTER_RADIUS)
damageOrigin += Vector( 0, 0, 128 )
}
break
case eDamageSourceId.switchback_trap:
if ( IsValid( victim ) && victim.IsPlayer() )
damageOrigin = victim.EyePosition() + (RandomVecInDome( Vector( 0, 0, -1 ) ) * SMOKESCREEN_DAMAGE_OUTER_RADIUS)
break
default:
if ( damageInfo.GetAttacker() )
{
inflictor = damageInfo.GetAttacker()
damageOrigin = inflictor.GetWorldSpaceCenter()
}
break
}
return damageOrigin
}
function TrackDPS( ent )
{
ent.s.dpsTracking <- {}
ent.s.dpsTracking.damage <- 0
local startTime = Time()
ent.WaitSignal( "Doomed" )
local duration = Time() - startTime
printt( "DPS:", ent.s.dpsTracking.damage / duration, duration )
delete ent.s.dpsTracking
}
function UpdateDPS( ent, damageInfo )
{
if ( ent.GetDoomedState() )
return
if ( !( "dpsTracking" in ent.s ) )
thread TrackDPS( ent )
ent.s.dpsTracking.damage += damageInfo.GetDamage()
}
function PlayerTookDamage( player, damageInfo, attacker, inflictor, damageSourceId )
{
local hitBox = damageInfo.GetHitBox()
local critHit = false
if ( CritWeaponInDamageInfo( damageInfo ) )
critHit = IsCriticalHit( attacker, player, hitBox, damageInfo.GetDamage(), damageInfo.GetDamageType() )
local damageType = damageInfo.GetCustomDamageType()
if ( critHit )
{
damageType = damageType | DF_CRITICAL
damageInfo.SetCustomDamageType( damageType )
}
local weaponMods = GetWeaponModsFromDamageInfo( damageInfo )
local eModSourceID = null
foreach ( mod in weaponMods )
{
local modSourceID = GetModSourceID( mod )
if( modSourceID != null && modSourceID in modNameStrings )
eModSourceID = modSourceID
if ( mod in level.burnCardWeaponModList )
{
damageType = damageType | DF_BURN_CARD_WEAPON
damageInfo.SetCustomDamageType( damageType )
break
}
}
local isTitan = player.IsTitan()
local shieldDamage = 0
local becameDoomed = false
if ( isTitan )
{
local doomedState = player.GetDoomedState()
shieldDamage = Titan_PlayerTookDamage( player, damageInfo, attacker, critHit )
becameDoomed = (!doomedState && player.GetDoomedState())
}
else
{
Wallrun_PlayerTookDamage( player, damageInfo, attacker )
}
if ( damageInfo.GetDamage() == 0 )
{
if ( becameDoomed ) //becameDoomed check is to make kill card come up for you even if you have auto-eject. In Titan_PlayerTookDamage we set damage to 0 if you have Auto-Eject and are doomed
TellClientPlayerTookDamage( player, damageInfo, attacker, eModSourceID, damageType, damageSourceId, becameDoomed )
return shieldDamage
}
local attackerOrigin = Vector( 0, 0, 0 )
if ( IsValid( attacker ) )
attackerOrigin = attacker.GetOrigin()
local weapon = damageInfo.GetWeapon()
if ( IsValid( weapon ) && "damagePush" in weapon.s )
{
local direction = player.GetOrigin() - ( weapon.GetOrigin() )
direction.Normalize()
PushEntWithDamageInfo( player, damageInfo, weapon.s.damagePush, direction )
}
// Get damage type again, because it may have been updated in one of the function calls above
damageType = damageInfo.GetCustomDamageType()
if ( IsAlive( player ) )
{
local storeEnt
if ( isTitan )
storeEnt = player.GetTitanSoul()
else
storeEnt = player
StoreDamageHistoryAndUpdate( storeEnt, MAX_DAMAGE_HISTORY_TIME, damageInfo.GetDamage(), attackerOrigin, damageType, damageSourceId, attacker, weaponMods )
}
if ( !(damageInfo.GetCustomDamageType() & DF_DOOMED_HEALTH_LOSS) )
TellClientPlayerTookDamage( player, damageInfo, attacker, eModSourceID, damageType, damageSourceId, becameDoomed )
return shieldDamage
}
function TellClientPlayerTookDamage( player, damageInfo, attacker, eModSourceID, damageType, damageSourceId, becameDoomed )
{
if ( !player.hasConnected )
return
local attackerEHandle = IsValid( attacker ) ? attacker.GetEncodedEHandle() : null
local weaponEHandle = IsValid( damageInfo.GetWeapon() ) ? damageInfo.GetWeapon().GetEncodedEHandle() : null
local damageOrigin = GetDamageOrigin( damageInfo, player )
if ( player.IsTitan() )
Remote.CallFunction_Replay( player, "ServerCallback_TitanTookDamage", damageInfo.GetDamage(), damageOrigin.x, damageOrigin.y, damageOrigin.z, damageType, damageSourceId, attackerEHandle, eModSourceID, becameDoomed )
else
Remote.CallFunction_Replay( player, "ServerCallback_PilotTookDamage", damageInfo.GetDamage(), damageOrigin.x, damageOrigin.y, damageOrigin.z, damageType, damageSourceId, attackerEHandle, eModSourceID )
}
// This only handles damage events. Whizbys can still cause snipercam to trigger without passing through this check.
function CodeCallBack_ShouldTriggerSniperCam( damageInfo )
{
switch ( damageInfo.GetDamageSourceIdentifier() )
{
case eDamageSourceId.titan_step:
case eDamageSourceId.titan_melee:
case eDamageSourceId.super_electric_smoke_screen:
return false
}
return true
}
function CodeCallback_ForceAIMissPlayer( npc, player )
{
if ( GetGameState() >= eGameState.Postmatch ) //Force AI to miss when controls are frozen
return true
if ( player.IsTitan() )
return false
local lethality = Riff_AILethality()
if ( lethality <= eAILethality.Default )
{
if ( GetTitanSoulBeingRodeoed( player ) != null )
return true
}
if ( player.ContextAction_IsActive() )
{
// less chance to hit "involved" player
return RandomFloat( 0, 1 ) >= 0.25
}
if ( player.IsFastPlayer() )
{
local frac
switch( lethality )
{
case eAILethality.Default:
if ( npc.IsSpectre() )
frac = 0.25
else
frac = 0.1
break
case eAILethality.TD_Low:
case eAILethality.TD_Medium:
case eAILethality.TD_High:
case eAILethality.High:
frac = 0.5
break
case eAILethality.VeryHigh:
frac = 0.75
break
}
return RandomFloat( 0, 1 ) >= frac
}
return false
}
function CodeCallback_OnTouchHealthKit( ent )
{
}
function ShouldPlayEMPEffectEvenWhenDamageIsZero( ent, attacker )
{
if ( ent.IsTitan() && IsTitanWithinBubbleShield( ent ) )
return false
if ( !IsValid( attacker ) )
return true
if ( attacker.GetTeam() != ent.GetTeam() )
return true
return false
}
function CodeCallback_SpawnDummyPilot_or_Titan( isTitan )
{
thread SpawnDummyPilotOrTitan(isTitan)
}
function SpawnDummyPilotOrTitan(isTitan)
{
if( isTitan == false)
{
FlagSet( "PilotBot" )
}
else
{
FlagClear("PilotBot")
}
local player = GetPlayerArray()[0]
local team = GetOtherTeam( player.GetTeam() )
if ( GAMETYPE == COOPERATIVE )
team = TEAM_MILITIA
if( team == TEAM_MILITIA)
ServerCommand( "bot_dummy -team 3" )
else
ServerCommand( "bot_dummy -team 2" )
}