diff options
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut')
-rw-r--r-- | Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut | 2179 |
1 files changed, 0 insertions, 2179 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut b/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut deleted file mode 100644 index a4c6e187b..000000000 --- a/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut +++ /dev/null @@ -1,2179 +0,0 @@ -untyped - -globalize_all_functions - -//******************************************************************************************** -// Base Gametype -//******************************************************************************************** -const DEATH_CHAT_DELAY = 0.3 - -global struct OutOfBoundsDataStruct //Have to globalize it because all functions are globalized in this file :/ -{ - int outOfBoundsTriggersTouched = 0 - float timeBackInBound = 0 - float timeLeftBeforeDyingFromOutOfBounds = OUT_OF_BOUNDS_TIME_LIMIT -} - -struct -{ - PilotLoadoutDef& playbackBotLoadout - array<entity> outOfBoundsTriggers = [] - array<entity> hurtTriggers = [] - bool functionref( entity, entity, var ) isProtectedFromFriendlyFire - table< entity, OutOfBoundsDataStruct > outOfBoundsTable -} file - -function BaseGametype_Init() -{ - FlagInit( "APlayerHasSpawned" ) - FlagInit( "PilotBot" ) - - if ( !reloadingScripts ) - { - level.gameTypeText <- null - level.classTypeText <- null - - level.titanAlwaysAvailableForTeam <- [ 0, 0, 0, 0 ] - - level.missingPlayersTimeout <- null - - CreateTeamColorControlPoints() - - AddClientCommandCallback( "CC_SelectRespawn", ClientCommand_SelectRespawn ) - AddClientCommandCallback( "CC_RespawnPlayer", ClientCommand_RespawnPlayer ) - - AddCallback_NPCLeeched( OnNPCLeeched ) - - MarkTeamsAsBalanced_Off() - } - - if ( IsSingleplayer() ) - { - file.isProtectedFromFriendlyFire = IsProtectedFromFriendlyFire_SP - } - else - { - file.isProtectedFromFriendlyFire = IsProtectedFromFriendlyFire_MP - } - - RegisterSignal( "OnDamageNotify" ) - RegisterSignal( "OnRespawned" ) - RegisterSignal( "ChoseToSpawnAsTitan" ) - RegisterSignal( "OutOfBounds" ) - RegisterSignal( "BackInBounds" ) - RegisterSignal( "PlayerKilled" ) - RegisterSignal( "RespawnMe" ) - RegisterSignal( "SimulateGameScore" ) - RegisterSignal( "ObserverThread" ) - RegisterSignal( "CE_FLAGS_CHANGED" ) - - RegisterSignal( "Stop_OnStartTouch_EntityOutOfBounds" ) - RegisterSignal( "Stop_OnEndTouch_EntityBackInBounds" ) - - RegisterSignal( "OnRespawnSelect" ) - - AddCallback_EntitiesDidLoad( BaseGametypeEntitiesDidLoad ) - - BaseGametype_Init_MPSP() - - AddCallback_OnTitanBecomesPilot( OnTitanBecomesPilot_OutOfBoundsCheck ) -} - -void function BaseGametypeEntitiesDidLoad() -{ - OutOfBoundsSetup() - TriggerHurtSetup() -} - -function CreateTeamColorControlPoints() -{ - Assert( !( "fx_CP_color_enemy" in level ) ) - Assert( !( "fx_CP_color_friendly" in level ) ) - - entity enemy = CreateEntity( "info_placement_helper" ) - SetTargetName( enemy, UniqueString( "teamColorControlPoint_enemy" ) ) - enemy.kv.start_active = 1 - DispatchSpawn( enemy ) - - enemy.SetOrigin( ENEMY_COLOR_FX ) - svGlobal.fx_CP_color_enemy = enemy - - entity friendly = CreateEntity( "info_placement_helper" ) - SetTargetName( friendly, UniqueString( "teamColorControlPoint_friendly" ) ) - friendly.kv.start_active = 1 - DispatchSpawn( friendly ) - - friendly.SetOrigin( FRIENDLY_COLOR_FX ) - svGlobal.fx_CP_color_friendly = friendly - - entity neutral = CreateEntity( "info_placement_helper" ) - SetTargetName( neutral, UniqueString( "teamColorControlPoint_neutral" ) ) - neutral.kv.start_active = 1 - DispatchSpawn( neutral ) - - neutral.SetOrigin( NEUTRAL_COLOR_FX ) - svGlobal.fx_CP_color_neutral = neutral -} - -const SOLDIER_SOUND_PAIN = "npc_grunt_pain" - -void function CodeCallback_OnPrecache() -{ - if ( IsLobby() ) - return - - Assert( IsSingleplayer() || GAMETYPE in GAMETYPE_TEXT ) - - // these should be level specific in SP - PrecacheEntity( "npc_soldier" ) - PrecacheEntity( "turret" ) - - PrecacheEntity( "npc_dropship", DROPSHIP_MODEL ) - - //Scavenger ore models. Need to precache here instead of in gamemode scripts for vpk builds - //Removing for build - /*level.scavengerSmallRocks <- [ - $"models/rocks/rock_01_sandstone.mdl" - //$"models/rocks/rock_02_sandstone.mdl" - //$"models/rocks/rock_03_sandstone.mdl" - //$"models/rocks/single_rock_01.mdl" - //$"models/rocks/single_rock_02.mdl" - //$"models/rocks/single_rock_03.mdl" - //$"models/rocks/single_rock_04.mdl" - ] - - level.scavengerLargeRocks <- [ - $"models/rocks/rock_boulder_large_01.mdl" - //$"models/rocks/sandstone_rock01.mdl" - //$"models/rocks/sandstone_rock02.mdl" - //$"models/rocks/sandstone_rock03.mdl" - //$"models/rocks/sandstone_rock04.mdl" - //$"models/rocks/sandstone_rock05.mdl" - ] - - foreach ( model in level.scavengerSmallRocks ) - { - PrecacheModel( model ) - } - - foreach ( model in level.scavengerLargeRocks ) - { - PrecacheModel( model ) - }*/ - - if ( !IsMenuLevel() ) - { - InitGameState() - SetGameState( eGameState.WaitingForPlayers ) - } - - level.ui.disableDev = IsMatchmakingServer() -} - -function AddFlinch( entity attackedEnt, damageInfo ) -{ - Assert( IsValid_ThisFrame( attackedEnt ) ) - - //if ( !( "nextFlinchTime" in attackedEnt.s ) ) - // attackedEnt.s.nextFlinchTime <- 0 - //if ( Time() < attackedEnt.s.nextFlinchTime ) - // return - //attackedEnt.s.nextFlinchTime = Time() + RandomFloatRange( 2.0, 4.0 ) - - vector damageAngles = VectorToAngles( DamageInfo_GetDamageForce( damageInfo ) ) - vector entAngles = attackedEnt.EyeAngles() - - float damageYaw = (damageAngles.y + 180) - entAngles.y - - damageYaw = AngleNormalize( damageYaw ) - - if ( damageYaw < 0 ) - damageYaw += 360 - - if ( damageYaw < 45 ) - DamageInfo_SetFlinchDirection( damageInfo, FLINCH_DIRECTION_BACKWARDS ); - else if ( damageYaw < 135 ) - DamageInfo_SetFlinchDirection( damageInfo, FLINCH_DIRECTION_RIGHT ); - else if ( damageYaw < 225 ) - DamageInfo_SetFlinchDirection( damageInfo, FLINCH_DIRECTION_FORWARDS ); - else if ( damageYaw < 315 ) - DamageInfo_SetFlinchDirection( damageInfo, FLINCH_DIRECTION_LEFT ); - else - DamageInfo_SetFlinchDirection( damageInfo, FLINCH_DIRECTION_BACKWARDS ); -} - - -bool function IsProtectedFromFriendlyFire_MP( entity attacker, entity ent, var damageInfo ) -{ - // no suicide protection - if ( attacker == ent ) - return false - - if ( attacker.GetTeam() != ent.GetTeam() ) - return false - - if ( DamageIgnoresFriendlyFire( damageInfo ) ) - return false - - if ( ent.GetOwner() != attacker && ent.GetBossPlayer() != attacker ) - return true - - if ( ent.e.noOwnerFriendlyFire == true ) - return true - - if ( ent.IsNPC() && ent.ai.preventOwnerDamage ) - return true - - return false -} - -bool function IsProtectedFromNPCFire( entity attacker, entity ent ) -{ - if ( attacker == ent ) - return false - if ( attacker.IsNPC() && ent.IsNPC() && ent.ai.invulnerableToNPC == true ) - return true - return false -} - - -bool function IsProtectedFromFriendlyFire_SP( entity attacker, entity ent, var damageInfo ) -{ - // no suicide protection - if ( attacker == ent ) - return false - - if ( attacker.GetTeam() == ent.GetTeam() ) - { - if ( attacker.IsNPC() ) - { - // dont titanfall me! - if ( ent.IsPlayer() ) - return true - - // bullets dont damage same team of npcs - if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_BULLET ) - return true - } - else if ( attacker.IsPlayer() ) - { - if ( ent.IsNPC() ) - { - if ( ent.IsTitan() ) - return true - - return !ent.AISetting_ShootableByFriendlyPlayer() - } - if ( ent.IsProjectile() ) - return false - return true - } - - if ( DamageIgnoresFriendlyFire( damageInfo ) ) - return false - - if ( ent.IsNPC() && ent.ai.preventOwnerDamage ) - { - if ( attacker == ent.GetOwner() || attacker == ent.GetBossPlayer() ) - return true - } - } - - return false -} - -bool function DamageIgnoresFriendlyFire( damageInfo ) -{ - if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS ) - return true - - int damageSourceID = DamageInfo_GetDamageSourceIdentifier( damageInfo ) - - switch ( damageSourceID ) - { - case eDamageSourceId.switchback_trap: - case eDamageSourceId.suicideSpectreAoE: - case eDamageSourceId.mp_titanweapon_stun_laser: // for energy transfer functionality. Preventing FF damage in the callback. - case eDamageSourceId.mp_titanability_smoke: // For FD Vanguard Shield Upgrades. Preventing FF damage in the callback. - return true - } - - return false -} - -bool function ScriptCallback_ShouldEntTakeDamage( entity ent, damageInfo ) -{ - if ( ent.IsInvulnerable() ) - return false - - entity attacker = DamageInfo_GetAttacker( damageInfo ) - entity inflictor = DamageInfo_GetInflictor( damageInfo ) - bool entIsPlayer = ent.IsPlayer() - - if ( !attacker ) - return false - - int damageType = DamageInfo_GetCustomDamageType( damageInfo ) - - if ( attacker == ent || IsValid( inflictor ) && inflictor == ent ) - { - if ( (damageType & DF_NO_SELF_DAMAGE) > 0 ) - return false - } - - if ( file.isProtectedFromFriendlyFire( attacker, ent, damageInfo ) ) - return false - - if ( IsProtectedFromNPCFire( attacker, ent ) ) - return false - - if ( !ShouldEntTakeDamage_SPMP( ent, damageInfo ) ) - return false - - if ( ent.IsTitan() ) - { - const int BULLET_VORTEX_FLAGS = (DF_VORTEX_REFIRE | DF_BULLET) - if ( ((damageType & BULLET_VORTEX_FLAGS) == BULLET_VORTEX_FLAGS) && (ent == attacker) ) - return false // don't let vortex-refiring titan hit themselves with bullet or bullet splash damage - - if ( IsTitanWithinBubbleShield( ent ) && TitanHasBubbleShieldWeapon( ent ) && !(damageType & DF_DOOMED_HEALTH_LOSS) ) - return false - } - - if ( IsTitanCrushDamage( damageInfo ) ) - { - if ( attacker.IsPhaseShifted() ) - return false - } - - if ( (inflictor != null) ) - { - if ( inflictor.IsProjectile() ) - { - entity attacker = DamageInfo_GetAttacker( damageInfo ) - if ( attacker == ent ) - { - bool shouldDamageOwner = inflictor.GetProjectileWeaponSettingBool( eWeaponVar.explosion_damages_owner ) - if ( !shouldDamageOwner ) - return false - - if ( entIsPlayer ) - { - array<string> mods = inflictor.ProjectileGetMods() - foreach ( mod in mods ) - { - if ( mod == "jump_kit" ) - { - float damageAmount = DamageInfo_GetDamage( damageInfo ) - damageAmount *= 0.75 - DamageInfo_SetDamage( damageInfo, damageAmount ) - // DamageInfo_SetDamageForce( damageInfo, DamageInfo_GetDamageForce( damageInfo ) * 2.0 ) - } - } - } - } - } - - if ( inflictor.e.onlyDamageEntitiesOnce == true && inflictor.e.damagedEntities.contains( ent ) ) - return false - - if ( inflictor.e.onlyDamageEntitiesOncePerTick == true ) - { - float currentTime = Time() - if ( currentTime != inflictor.e.lastDamageTickTime ) - { - inflictor.e.damagedEntities.clear() - inflictor.e.lastDamageTickTime = currentTime - } - else if ( inflictor.e.damagedEntities.contains( ent ) ) - { - return false - } - } - } - - if ( ent.IsPlayer() ) - { - return ShouldPlayerTakeDamage( ent, damageInfo ) - } - - return true -} - -bool function ShouldPlayerTakeDamage( entity player, damageInfo ) -{ - if ( player.IsGodMode() ) - return false - - if ( player.IsPhaseShifted() - && !(DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS) - && !IsDamageFromDamageTrigger( damageInfo ) ) - return false - - if ( player.IsInvulnerable() ) - return false - - if ( player.IsTitan() ) - { - return true - } - else - { - //Rodeo cases - entity titanSoul = player.GetTitanSoulBeingRodeoed() - if ( IsValid( titanSoul ) ) - { - entity titan = titanSoul.GetTitan() - //Stop being stepped on by the guy you are rodeoing - if ( IsTitanCrushDamage( damageInfo ) && ( titan == DamageInfo_GetAttacker( damageInfo ) ) ) - return false - else - return true - } - else - { - return true - } - } - - unreachable -} - - -void function HandlePainSounds( entity ent, var damageInfo ) -{ - //exit if the thing is dead - if ( ent.GetHealth() < DamageInfo_GetDamage( damageInfo ) ) - return - - PlayPainSounds( ent, damageInfo ) -} - -float function GetHeadshotDamageMultiplierFromDamageInfo( var damageInfo ) -{ - entity weapon = DamageInfo_GetWeapon( damageInfo ) - if ( weapon ) - { - float result = weapon.GetWeaponSettingFloat( eWeaponVar.damage_headshot_scale ) - return result - } - - entity inflictor = DamageInfo_GetInflictor( damageInfo ) - if ( inflictor && inflictor.IsProjectile() ) - { - float result = inflictor.GetProjectileWeaponSettingFloat( eWeaponVar.damage_headshot_scale ) - return result - } - - return 1.0 -} - -function HandleLocationBasedDamage( entity ent, var damageInfo ) -{ - // Don't allow non-players to get headshots or any other location bonuses - entity attacker = DamageInfo_GetAttacker( damageInfo ) - if ( !IsValid( attacker ) || !attacker.IsPlayer() ) - return - - bool debugPrints = false - int hitGroup = DamageInfo_GetHitGroup( damageInfo ) - - if ( debugPrints ) - { - printt( "---------------------" ) - printt( "LOCATION BASED DAMAGE" ) - printt( "HIDGROUP ID:", hitGroup ) - if ( hitGroup == HITGROUP_GENERIC ) - printt( "HITGROUP: HITGROUP_GENERIC" ) - else if ( hitGroup == HITGROUP_HEAD ) - printt( "HITGROUP: HITGROUP_HEAD" ) - else if ( hitGroup == HITGROUP_CHEST ) - printt( "HITGROUP: HITGROUP_CHEST" ) - else if ( hitGroup == HITGROUP_STOMACH ) - printt( "HITGROUP: HITGROUP_STOMACH" ) - else if ( hitGroup == HITGROUP_LEFTARM ) - printt( "HITGROUP: HITGROUP_LEFTARM" ) - else if ( hitGroup == HITGROUP_RIGHTARM ) - printt( "HITGROUP: HITGROUP_RIGHTARM" ) - else if ( hitGroup == HITGROUP_LEFTLEG ) - printt( "HITGROUP: HITGROUP_LEFTLEG" ) - else if ( hitGroup == HITGROUP_RIGHTLEG ) - printt( "HITGROUP: HITGROUP_RIGHTLEG" ) - else if ( hitGroup == HITGROUP_GEAR ) - printt( "HITGROUP: HITGROUP_GEAR" ) - else - printt( "HITGROUP: UNKNOWN" ) - } - - bool isValidHeadShot = IsValidHeadShot( damageInfo, ent ) - if ( isValidHeadShot ) - DamageInfo_AddCustomDamageType( damageInfo, DF_HEADSHOT ) - - float damageMult_location = 1.0 - - var weaponName // TODO: If set to type string, will cause errors because weaponName can be "" - if ( DamageInfo_GetWeapon( damageInfo ) ) - weaponName = DamageInfo_GetWeapon( damageInfo ).GetWeaponClassName() - else if ( DamageInfo_GetInflictor( damageInfo ) && (DamageInfo_GetInflictor( damageInfo ) instanceof CProjectile ) ) - weaponName = DamageInfo_GetInflictor( damageInfo ).ProjectileGetWeaponClassName() - - if ( ent.IsTitan() ) - { - damageMult_location = GetCriticalScaler( ent, damageInfo ) - } - else if ( IsSuperSpectre( ent ) ) - { - if ( CritWeaponInDamageInfo( damageInfo ) && IsCriticalHit( attacker, ent, DamageInfo_GetHitBox( damageInfo ), DamageInfo_GetDamage( damageInfo ), DamageInfo_GetDamageType( damageInfo ) ) ) - { - damageMult_location = GetCriticalScaler( ent, damageInfo ) - DamageInfo_AddCustomDamageType( damageInfo, DF_CRITICAL ) - } - } - else if ( IsStalker( ent ) ) - { - // note: stalker location based damage is done in _ai_stalker.gnut. - switch ( hitGroup ) - { - case HITGROUP_GEAR: - DamageInfo_AddCustomDamageType( damageInfo, DF_CRITICAL ) - break - } - } - else if ( isValidHeadShot ) - { - damageMult_location = GetHeadshotDamageMultiplierFromDamageInfo( damageInfo ) - } - - // modify damage value based on where we hit - if ( damageMult_location != 1.0 ) - { - if ( debugPrints ) - { - printt( "Multiplier:", damageMult_location ) - printt( "---------------------" ) - } - - DamageInfo_ScaleDamage( damageInfo, damageMult_location ) - } -} - -function PlayerDamageFeedback( entity ent, damageInfo ) -{ -// printt( "player damage feedback for " + ent ) - entity attacker = DamageInfo_GetAttacker( damageInfo ) - Assert( attacker.IsPlayer() ) - - int customDamageType = DamageInfo_GetCustomDamageType( damageInfo ) - - if ( IsMaxRangeShot( damageInfo ) ) - customDamageType = customDamageType | DF_MAX_RANGE - - if ( ent.GetHealth() - DamageInfo_GetDamage( damageInfo ) <= 0 ) - { - if ( !ent.IsNPC() || ent.ai.killShotSound ) - customDamageType = customDamageType | DF_KILLSHOT - } - - attacker.NotifyDidDamage( ent, DamageInfo_GetHitBox( damageInfo ), DamageInfo_GetDamagePosition( damageInfo ), customDamageType, DamageInfo_GetDamage( damageInfo ), DamageInfo_GetDamageFlags( damageInfo ), DamageInfo_GetHitGroup( damageInfo ), DamageInfo_GetWeapon( damageInfo ), DamageInfo_GetDistFromAttackOrigin( damageInfo ) ) -} - -void function UpdateLastDamageTime( entity ent ) -{ - if ( !ent.IsPlayer() ) - return - - ent.p.lastDamageTime = Time() -} - -void function PlayerDealtTitanDamage( entity attacker, entity victim, float savedDamage, var damageInfo ) -{ - if ( attacker != victim ) - { - attacker.p.titanDamageDealt += savedDamage - -#if MP - UpdateTitanWeaponDamageStat( attacker, savedDamage, damageInfo ) - - if ( attacker.IsTitan() ) - { - attacker.p.titanDamageDealt_Stat += savedDamage - if ( attacker.p.titanDamageDealt_Stat >= 500 ) // buffer the titan stat damage so that we don't spam damage callbacks - { - UpdateTitanDamageStat( attacker, attacker.p.titanDamageDealt_Stat, damageInfo ) - attacker.p.titanDamageDealt_Stat = 0 - } - } -#endif - } -} - -function UpdateAttackerInfo( entity ent, entity attacker, damage ) -{ - entity attackerPlayer = GetPlayerFromEntity( attacker ) - if ( !attackerPlayer ) - return - - // cannot be your own last attacker - if ( attackerPlayer == ent ) - return - - if ( !damage || damage <= 0 ) - return - - if ( !("attackerInfo" in ent.s) ) - ent.s.attackerInfo <- {} - else if ( ent.GetHealth() == ent.GetMaxHealth() ) - ent.s.attackerInfo.clear() - - if ( !(attackerPlayer.weakref() in ent.s.attackerInfo ) ) - ent.s.attackerInfo[attackerPlayer.weakref()] <- 0 - - ent.s.attackerInfo[attackerPlayer.weakref()] += damage - - ent.e.lastAttacker = attackerPlayer -} - -entity function GetAttackerPlayerOrBossPlayer( entity attacker ) -{ - if ( !IsValid( attacker ) ) - return null - - if ( attacker.IsPlayer() ) - return attacker - - entity bossPlayer = attacker.GetBossPlayer() - if ( !IsValid( bossPlayer ) ) - return null - - return bossPlayer -} - -entity function GetAttackerOrLastAttacker( entity ent, damageInfo ) -{ - entity attacker = DamageInfo_GetAttacker( damageInfo ) - - if ( ShouldGetLastAttacker( ent, attacker ) == false ) - return attacker - - entity lastAttacker = GetLastAttacker( ent ) //Attacker doesn't work, get last attacker - - if ( IsValid( lastAttacker ) == true ) - return lastAttacker - - //last attacker doesn't work, get latestAssistingPlayerInfo - AssistingPlayerStruct attackerInfo = GetLatestAssistingPlayerInfo( ent ) - if ( IsValid( attackerInfo.player ) ) - return attackerInfo.player - - if ( IsValid( attacker ) ) //No Last Attacker and No Lastest Assisting Player, e.g. when you suicide before taking damage. Just return the attacker if valid - return attacker - - return null -} - -bool function ShouldGetLastAttacker( entity ent, entity attacker ) -{ - if ( IsValid( attacker ) == false ) - return true - - if ( attacker == ent ) //suicide - return true - - if ( attacker.IsPlayer() == false && attacker.IsNPC() == false ) //Environmental damage - return true - - return false -} - -function ClearLastAttacker( entity ent ) -{ - ent.e.lastAttacker = null -} - -entity function GetLastAttacker( entity ent ) -{ - if ( ent.IsTitan() && IsValid( ent.GetTitanSoul() ) ) // JFS: second check is defensive - { - entity soul = ent.GetTitanSoul() - if ( soul.lastAttackInfo && "attacker" in soul.lastAttackInfo && IsValid( soul.lastAttackInfo.attacker ) ) - return expect entity( soul.lastAttackInfo.attacker ) - } - - if ( !IsValid( ent.e.lastAttacker ) ) - return null - - return ent.e.lastAttacker -} - -bool function PlayerOrNPCKilled( entity ent, var damageInfo ) -{ - bool gamePlayingOrSuddenDeath = GamePlayingOrSuddenDeath() // Storing this off here, the game state can change in the callbacks below which may cause kills to not count - - int damageSourceID = DamageInfo_GetDamageSourceIdentifier( damageInfo ) - - if ( damageSourceID == eDamageSourceId.round_end ) - return false - - entity attacker = GetAttackerOrLastAttacker( ent, damageInfo ) - if ( !IsValid( attacker ) ) - return false - - if ( ent.IsPlayer() ) - { - LogPlayerMatchStat_Death( ent ) - - if ( attacker.IsPlayer() && (attacker != ent) ) - LogPlayerMatchStat_KilledAPilot( attacker ) - } - - if ( ent.IsNPC() && !IsValidNPCTarget( ent ) ) - return false - - if ( !attacker.IsPlayer() ) - { - entity newAttacker = GetPlayerFromEntity( attacker ) - if ( IsValid( newAttacker ) ) - attacker = newAttacker - } - - if ( ent.IsPlayer() ) - { - //Do callbacks. Main reason we call this here as opposed to CodeCallback_OnPlayerKilled() is legacy script compatibility reasons. - //For example: In script immediately above this we change the attacker to get the player behind the kill, e.g. owner of a pet titan, etc. Bunch of registered callbacks depends on this. - foreach( callbackFunc in svGlobal.onPlayerKilledCallbacks ) - callbackFunc( ent, attacker, damageInfo ) - } - else if ( ent.IsNPC() ) - { - //Do callbacks. Main reason we call this here as opposed to CodeCallback_OnNPCKilled() is legacy script compatibility reasons. - //For example: In script immediately above this we change the attacker to get the player behind the kill, e.g. owner of a pet titan, etc. Bunch of registered callbacks depends on this. - foreach( callbackFunc in svGlobal.onNPCKilledCallbacks ) - { - callbackFunc( ent, attacker, damageInfo ) - } - } - - if ( ent.IsTitan() ) - { - thread TitanVO_DelayedTitanDown( ent ) - } - - if ( !attacker.IsPlayer() ) - { - // This gets the last player that did damage to the entity so that we can give him the kill - AssistingPlayerStruct attackerInfo = GetLatestAssistingPlayerInfo( ent ) - attacker = attackerInfo.player - - if ( !IsValid( attacker ) ) - return true - - // Hack - attacker history isn't on client to calculate if a player should get credit for a kill when AI steals the final killing shot while a player is damaging them. - array<entity> playerArray = GetPlayerArray() - foreach ( player in playerArray ) - { - Remote_CallFunction_Replay( player, "ServerCallback_SetAssistInformation", attackerInfo.damageSourceId, attacker.GetEncodedEHandle(), ent.GetEncodedEHandle(), attackerInfo.assistTime ) - } - } - - // player attacker only from here down - - PreScoreEventUpdateStats( attacker, ent ) - if ( ent.GetTeam() != attacker.GetTeam() ) - { - if ( ent.IsPlayer() ) - ScoreEvent_PlayerKilled( ent, attacker, damageInfo ) - else if ( ent.IsTitan() && ent.IsNPC() ) - ScoreEvent_TitanKilled( ent, attacker, damageInfo ) - else - ScoreEvent_NPCKilled( ent, attacker, damageInfo ) - } - PostScoreEventUpdateStats( attacker, ent ) - - if ( ent.GetTeam() == attacker.GetTeam() ) - { - return false - } - - // Respawn Kill INFECTION!! - if ( ent.IsPlayer() && attacker.IsPlayer() ) - { - if ( ent.GetPersistentVar( "respawnKillInfected" ) && !attacker.GetPersistentVar( "respawnKillInfected" ) ) - attacker.SetPersistentVar( "respawnKillInfected", true ) - } - - if ( gamePlayingOrSuddenDeath ) - { - if ( ent.IsPlayer() ) - { - if ( ent.IsTitan() ) - { - //if we killed a player in a titan count two kills (one for the pilot, one for the titan ) - attacker.AddToPlayerGameStat( PGS_KILLS, 2 ) - attacker.AddToPlayerGameStat( PGS_TITAN_KILLS, 1 ) - attacker.AddToPlayerGameStat( PGS_PILOT_KILLS, 1 ) - } - else - { - attacker.AddToPlayerGameStat( PGS_KILLS, 1 ) - attacker.AddToPlayerGameStat( PGS_PILOT_KILLS, 1 ) - } - } - else - { - if ( ent.IsTitan() ) - attacker.AddToPlayerGameStat( PGS_TITAN_KILLS, 1 ) - - if( !IsMarvin( ent ) && !ent.IsTitan() ) - attacker.AddToPlayerGameStat( PGS_NPC_KILLS, 1 ) - } - } - - return true -} - -// used to calculate build time credit in special cases. Cloak Drones and Suicide Spectres use it for now. -float function CalculateBuildTimeCredit( entity attacker, entity target, float damage, int health, int maxHealth, string playlistVarStr, float defaultCredit ) -{ - float titanSpawnDelay = GetTitanBuildTime( attacker ) - float timerCredit = 0 - - health = maxint( 0, health ) // health should never be less then 0 - if ( titanSpawnDelay && IsAlive( target ) ) - { - timerCredit = GetCurrentPlaylistVarFloat( playlistVarStr, defaultCredit ) - - float dealtDamage = min( health, damage ) - timerCredit = timerCredit * (dealtDamage / maxHealth ) - } - - return timerCredit -} - -function UpdateNextRespawnTime( entity player, float time ) -{ - player.nv.nextRespawnTime = time -} - -bool function ShouldSetObserverTarget( entity attacker ) -{ - if ( !IsAlive( attacker ) ) - return false - - if ( attacker.IsPlayer() && attacker.IsObserver() ) - return false - - return true -} - -float function CalculateLengthOfKillReplay( entity player, int methodOfDeath ) //Meant to be called on the same frame player dies -{ - return GetDeathCamLength( player ) + GetKillReplayBeforeTime( player, methodOfDeath ) + GetKillReplayAfterTime( player ) -} - -float function GetKillReplayBeforeTime( entity player, int methodOfDeath ) -{ - switch ( methodOfDeath ) - { - case eDamageSourceId.damagedef_titan_fall: - case eDamageSourceId.damagedef_titan_hotdrop: - case eDamageSourceId.damagedef_reaper_fall: - case eDamageSourceId.droppod_impact: - return KILL_REPLAY_BEFORE_KILL_TIME_DROPPOD - } - - if ( !GamePlayingOrSuddenDeath() ) - return KILL_REPLAY_BEFORE_KILL_TIME_SHORT - - float titanKillReplayTime = KILL_REPLAY_BEFORE_KILL_TIME_TITAN - float pilotKillReplayTime = KILL_REPLAY_BEFORE_KILL_TIME_PILOT - switch ( methodOfDeath ) - { - case eDamageSourceId.titan_execution: - return titanKillReplayTime + 3.0 - - case eDamageSourceId.switchback_trap: - if ( player.IsTitan() ) - return titanKillReplayTime + 6.0 - else - return pilotKillReplayTime + 8.0 - } - - if ( player.IsTitan() ) - return titanKillReplayTime - - // titan recently? - if ( Time() - player.lastTitanTime < 5.0 ) - return titanKillReplayTime - - return pilotKillReplayTime -} - -function TrackDestroyTimeForReplay( entity attacker, table replayTracker ) -{ - float startTime = Time() - // tracks the time until the attacker becomes invalid - EndSignal( replayTracker, "OnDestroy" ) - - OnThreadEnd( - function () : ( replayTracker, startTime ) - { - replayTracker.validTime = Time() - startTime - } - ) - - string signal = "OnDestroy" - - if ( IsAlive( attacker ) ) - attacker.WaitSignal( signal ) - else - WaitSignalOnDeadEnt( attacker, signal ) -} - -#if MP -function PlayerWatchesKillReplay( entity player, int inflictorEHandle, int attackerViewIndex, float timeSinceAttackerSpawned, float timeOfDeath, float beforeTime, table replayTracker ) -{ - OnThreadEnd( - function () : ( player, replayTracker ) - { - Signal( replayTracker, "OnDestroy" ) - } - ) - - player.EndSignal( "RespawnMe" ) - - float timeBeforeKill = beforeTime - float timeAfterKill = GetKillReplayAfterTime( player ) - - if ( timeBeforeKill > timeSinceAttackerSpawned ) - timeBeforeKill = timeSinceAttackerSpawned - - float replayDelay = timeBeforeKill + ( Time() - timeOfDeath ) - if ( replayDelay < 0 ) - { - print( "PlayerWatchesKillReplay(): replayDelay is < 0 (" + replayDelay + "). Aborting kill replay.\n" ) - return - } - - player.SetKillReplayDelay( replayDelay, THIRD_PERSON_KILL_REPLAY_ALWAYS ) - player.SetKillReplayInflictorEHandle( inflictorEHandle ) - player.SetKillReplayVictim( player ) - player.SetViewIndex( attackerViewIndex ) - - wait timeBeforeKill - - if ( replayTracker.validTime != null && replayTracker.validTime < timeAfterKill ) - { - float waitTime = expect float( replayTracker.validTime ) - 0.1 // cut off just before ent becomes invalid in the past - if ( waitTime > 0 ) - wait waitTime - } - else - { - wait timeAfterKill - } -} -#endif // #if MP - -bool function ClientCommand_SelectRespawn( entity player, array<string> args ) -{ - if ( IsAlive( player ) ) - return true - - if ( args.len() == 0 ) - return true - - int index = args[ 0 ].tointeger() - - switch ( index ) - { - case 1: - player.SetPersistentVar( "spawnAsTitan", true ) - break - case 2: - player.SetPersistentVar( "spawnAsTitan", false ) - break - } - - return true -} - - -bool function ClientCommand_RespawnPlayer( entity player, array<string>args ) -{ - if ( IsSingleplayer() ) - return true - - if ( IsAlive( player ) ) - return true - - if ( args.len() != 1 ) - return true - - string opParm = args[ 0 ] - - if ( opParm.find( "burncard" ) != null ) - { - //int burnCard = opParm.tointeger() - //SetPlayerBurnCardSlotToActivate( player, burnCard ) - return true - } - else if ( opParm == "Titan" ) - { - player.SetPersistentVar( "spawnAsTitan", true ) - } - else if ( opParm == "Pilot" ) - { - player.SetPersistentVar( "spawnAsTitan", false ) - } - - float deathCamLength = GetDeathCamLength( player ) - float skipBufferTime = 0.5 - if ( Time() > (player.p.postDeathThreadStartTime + deathCamLength) - skipBufferTime ) - { - player.s.respawnSelectionDone = true - player.Signal( "RespawnMe" ) - } - - return true -} - -function AIChatter( string alias, int team, vector origin ) -{ - array<entity> ai = GetNearbyFriendlyGrunts( origin, team ) - - if ( ai.len() > 0 ) - { - PlaySquadConversationToAll( alias, ai[0] ) - } -} - -const MAX_ACTIVITY_DISABLED = 0 -const MAX_ACTIVITY_PILOTS = 1 -const MAX_ACTIVITY_TITANS = 2 -const MAX_ACTIVITY_PILOTS_AND_TITANS = 3 -const MAX_ACTIVITY_CONGER_MODE = 4 - -bool function GetPilotBotFlag() -{ - // IMPORTANT: Please call this consistently instead of Flag( "PilotBot" ) - // Force titan or pilot bots according to max activity mode if it is enabled. - // Otherwise, leave the "pilotBot" flag alone and do what the game mode wants. - int max_activity_mode = GetConVarInt( "max_activity_mode" ) - if ( max_activity_mode == MAX_ACTIVITY_PILOTS || max_activity_mode == MAX_ACTIVITY_PILOTS_AND_TITANS ) - return true - else if ( max_activity_mode == MAX_ACTIVITY_TITANS ) - return false - else if ( max_activity_mode == MAX_ACTIVITY_CONGER_MODE ) - return rand() % 2 != 0 // conger mode: 50/50 pilot and titan bots! - else - return Flag( "PilotBot" ) - - unreachable -} - - -function DoRespawnPlayer( entity player, entity spawnPoint ) -{ - player.p.lastSpawnPoint = spawnPoint - player.RespawnPlayer( spawnPoint ) //This will send "OnRespawned" signal, killing the thread if started from PostDeathThread -} - -function SetupPilotSpawnOnRematch( entity player ) -{ - // clear respawn countdown message - if ( GetWaveSpawnType() == eWaveSpawnType.DROPSHIP ) - MessageToPlayer( player, eEventNotifications.Clear ) - - player.SetOrigin( player.p.rematchOrigin ) - - if ( GetWaveSpawnType() == eWaveSpawnType.DISABLED ) - wait 0.9 - - if ( IsAlive( player ) )//HACK: This seems terrible, we shouldn't have to do this - { - printt( "This happened one time, in retail." ) - return - } - - if ( ShouldGivePlayerInfoOnSpawn() ) - thread GivePlayerInfoOnSpawn( player ) - - return -} - -bool function ShouldGivePlayerInfoOnSpawn() -{ - return GetCurrentPlaylistVarInt( "minimap_sonar_pulse_on_respawn", 0 ) > 0 -} - -function GivePlayerInfoOnSpawn( entity player ) -{ - player.EndSignal( "OnDeath" ) - - //PrintFunc() - - while( player.IsWatchingKillReplay() ) - WaitFrame() - - //printt( " GivePlayerInfoOnSpawn Player isn't watching kill replay anymore!" ) - - wait 0.2 //Hack: Have to wait even though player should not be watching kill replay anymore... - - //This needed a wait, probably because at this time we haven't given them loadouts yet, so when we do give them loadouts it strips out the passive? - thread ScanMinimap( player, true, 0.5 ) //x second minimap pulse -} - -bool function ShouldStartSpawn( entity player ) -{ - if ( Riff_FloorIsLava() ) - return false - - if ( Flag( "ForceStartSpawn" ) ) - return true - - if ( Flag( "IgnoreStartSpawn" ) ) - return false - - if ( GetGameState() <= eGameState.Prematch ) - return true - - if ( player.s.respawnCount ) - return false - - return GameTime_PlayingTime() < START_SPAWN_GRACE_PERIOD -} - -void function PlayerSpawnsIntoPetTitan( entity player ) -{ - player.EndSignal( "OnDestroy" ) - - entity titan = player.GetPetTitan() - - vector origin = titan.GetOrigin() + Vector( 0, 0, 600 ) - vector angles = titan.GetAngles() - - entity camera = CreateTitanDropCamera( origin, Vector(90,angles.y,0) ) - player.SetViewEntity( camera, false ) - - player.isSpawning = true // set this to prevent .isSpawning checks from returning false - - angles.x = 70 - - player.SetOrigin( origin ) - player.SnapEyeAngles( angles ) - player.SetVelocity( Vector( 0.0, 0.0, 0.0 ) ) - - OnThreadEnd( - function() : ( player ) - { - if ( IsValid( player ) ) - { - player.ClearViewEntity() - player.ClearSpawnPoint() - player.isSpawning = null - } - } - ) - - wait 0.2 - - local criteria = { - embark = "above_close", - titanCanStandRequired = true - } - - local embarkAction - embarkAction = FindEmbarkActionForCriteria( criteria ) - if ( embarkAction == null ) - embarkAction = GetRandomEmbarkAction() - - if ( IsValid( camera ) ) - { - // camera can be invalid for a moment when server shuts down - // camera.FireNow( "Disable", "!activator", null, player ) - camera.Destroy() - } - - DoRespawnPlayer( player, null ) - - if ( PlayerCanSpawnIntoTitan( player ) ) - { - table action = expect table( GenerateEmbarkActionTable( player, titan, embarkAction ) ) - PlayerEmbarksTitan( player, titan, action ) - } -} - -entity function CreateTitanDropCamera( origin, angles ) -{ - entity viewControl = CreateEntity( "point_viewcontrol" ) - viewControl.kv.spawnflags = 56 // infinite hold time, snap to goal angles, make player non-solid - - viewControl.SetOrigin( origin ) - viewControl.SetAngles( angles ) - DispatchSpawn( viewControl ) - - return viewControl -} - -entity function CreateDropPodViewController( entity pod ) -{ - entity viewControl = CreateEntity( "point_viewcontrol" ) - viewControl.kv.spawnflags = 56 // infinite hold time, snap to goal angles, make player non-solid - - viewControl.SetOrigin( pod.GetOrigin() + Vector( 44, -64, 520 ) ) - float yaw = pod.GetAngles().y - viewControl.SetAngles( Vector( 90, yaw + 10, 0 ) ) - DispatchSpawn( viewControl ) - - viewControl.SetParent( pod ) - - return viewControl -} - - -function ClearEntInUseOnDestroy( dropPoint, dropPod ) -{ - dropPod.WaitSignal( "OnDestroy" ) - dropPoint.e.spawnPointInUse = false -} - -float function GetPlayerLastRespawnTime( entity player ) -{ - return expect float( player.s.respawnTime ) -} - -entity function GetEmbarkPlayer( entity titan ) -{ - if ( "embarkingPlayer" in titan.s ) - return expect entity( titan.s.embarkingPlayer ) - - return null -} - -entity function GetDisembarkPlayer( entity titan ) -{ - if ( "disembarkingPlayer" in titan.s ) - return expect entity( titan.s.disembarkingPlayer ) - - return null -} - -entity function GetEmbarkDisembarkPlayer( entity titan ) -{ - entity result = GetEmbarkPlayer( titan ) - - if ( IsValid( result ) ) - return result - - result = GetDisembarkPlayer( titan ) - if ( IsValid( result ) ) - return result - - return null -} - -void function CodeCallback_OnNPCKilled( entity npc, var damageInfo ) -{ - if ( IsSingleplayer() ) - { - OnNPCKilled_SP( npc, damageInfo ) - return - } - - HandleDeathPackage( npc, damageInfo ) - - if ( npc.IsTitan() ) - { - // if a player is getting in, kill him too - entity player = GetEmbarkPlayer( npc ) - if ( IsAlive( player ) ) - { - // kill the embarking player - //printt( "Killed embarking player" ) - KillFromInfo( player, damageInfo ) - } - - if ( !GetDoomedState( npc ) ) - { - // Added via AddCallback_OnTitanDoomed - foreach ( callbackFunc in svGlobal.onTitanDoomedCallbacks ) - { - callbackFunc( npc, damageInfo ) - } - } - } - - PlayerOrNPCKilled( npc, damageInfo ) -} - -void function OnNPCKilled_SP( entity npc, var damageInfo ) -{ - HandleDeathPackage( npc, damageInfo ) - - if ( npc.IsTitan() ) - { - // if a player is getting in, kill him too - entity player = GetEmbarkPlayer( npc ) - if ( IsAlive( player ) ) - { - // kill the embarking player - //printt( "Killed embarking player" ) - KillFromInfo( player, damageInfo ) - } - - if ( !GetDoomedState( npc ) ) - { - // Added via AddCallback_OnTitanDoomed - foreach ( callbackFunc in svGlobal.onTitanDoomedCallbacks ) - { - callbackFunc( npc, damageInfo ) - } - } - } - - entity attacker = GetAttackerOrLastAttacker( npc, damageInfo ) - if ( !IsValid( attacker ) ) - return - - if ( !attacker.IsPlayer() ) - { - entity newAttacker = GetPlayerFromEntity( attacker ) - if ( IsValid( newAttacker ) ) - attacker = newAttacker - } - - foreach( callbackFunc in svGlobal.onNPCKilledCallbacks ) - { - callbackFunc( npc, attacker, damageInfo ) - } - - if ( npc.IsTitan() ) - thread TitanVO_DelayedTitanDown( npc ) -} - -void function CodeCallback_OnEntityDestroyed( entity ent ) -{ - // Must do ent.SetDoDestroyCallback( true ) to get this callback -// print( "OnEntityDestroyed " + ent.entindex() + "\n" ) - - if ( "onEntityDestroyedCallbacks" in ent.s ) - { - foreach ( callbackFunc in ent.s.onEntityDestroyedCallbacks ) - { - callbackFunc( ent ) - } - } -} - -function AddEntityDestroyedCallback( ent, callbackFunc ) -{ - AssertParameters( callbackFunc, 1, "entity" ) - - if ( !( "onEntityDestroyedCallbacks" in ent.s ) ) - ent.s.onEntityDestroyedCallbacks <- [] - - ent.s.onEntityDestroyedCallbacks.append( callbackFunc ) - - // set this or else the ent won't run CodeCallback_OnEntityDestroyed at all - ent.SetDoDestroyCallback( true ) -} - -bool function WeaponInterruptsCloak( entity weapon ) -{ - if ( !IsValid( weapon ) ) - return false - - return weapon.GetWeaponInfoFileKeyField( "does_not_interrupt_cloak" ) != 1 -} - -void function CodeCallback_WeaponFireInCloak( entity player ) -{ - if ( !WeaponInterruptsCloak( player.GetActiveWeapon() ) ) - return - - if ( player.IsTitan() ) // Fix timing issue with auto-eject cloak and firing your weapon as a Titan cancelling it. This assumes we never want cloaked titans! - return - - // if ( player.cloakedForever ) - // { - // player.SetCloakFlicker( 1.0, 2.0 ) - // return - // } - - // // Check if we are allowed some cloaked shots based on ability selection - // if ( player.s.cloakedShotsAllowed > 0 ) - // { - // player.s.cloakedShotsAllowed-- - // return - // } - - if ( IsMultiplayer() ) - { - //player.SetCloakFlicker( 1.0, 2.0 ) - - DisableCloak( player, 0.5 ) - entity weapon = player.GetOffhandWeapon( OFFHAND_LEFT ) - //printt( "weapon", weapon.GetWeaponClassName() ) - // JFS; need code feature to properly reset next attack time/cooldown stuff - if ( IsValid( weapon ) && weapon.GetWeaponClassName() == "mp_ability_cloak" ) - { - player.TakeOffhandWeapon( OFFHAND_LEFT ) - player.GiveOffhandWeapon( "mp_ability_cloak", OFFHAND_LEFT ) - weapon = player.GetOffhandWeapon( OFFHAND_LEFT ) - weapon.SetWeaponPrimaryClipCountAbsolute( 0 ) - } - } - else - { - DisableCloak( player, 0.5 ) - } -} - -// need "you will change class next time" message -function OnPlayerCloseClassMenu( entity player ) -{ - if ( GetGameState() <= eGameState.Prematch ) - return - - if ( player.IsEntAlive() ) - return - - if ( player.s.inPostDeath ) - return - - if ( IsValid( player.isSpawning ) ) - return - - thread DecideRespawnPlayer( player ) // there is a wait that happens later when using rematch burncard in Frontier Defense. -} - -// playerconnected Reload -void function CodeCallback_OnClientReloadConnectionCompleted( entity player ) -{ - FinishClientScriptInitialization( player ) -} - - -bool function ShouldPlayerHaveLossProtection( entity player ) -{ - if ( level.nv.matchProgress < GetCurrentPlaylistVarInt( "matchLossProtectionThreshold", 10 ) ) - return false - - if ( IsPrivateMatch() ) - return false - - if ( IsFFAGame() ) - return true - - int team = player.GetTeam() - int otherTeam = GetOtherTeam( team ) - int teamScore = IsRoundBased() ? GameRules_GetTeamScore2( team ) : GameRules_GetTeamScore( team ) - int otherTeamScore = IsRoundBased() ? GameRules_GetTeamScore2( otherTeam ) : GameRules_GetTeamScore( otherTeam ) - - if ( teamScore < otherTeamScore ) - return true - - return false -} - -// This server will recieve this command from the client once they have loaded/run all of their scripts -// Any client hud initialization should be done here -function FinishClientScriptInitialization( entity player ) -{ - printt( "Player client script initialization complete: " + player ); - - player.p.clientScriptInitialized = true - - SyncServerVars( player ) - SyncEntityVars( player ) - SyncUIVars( player ) - - Remote_CallFunction_Replay( player, "ServerCallback_ClientInitComplete" ) -} - -function NotifyClientsOfConnection( entity player, state ) -{ - int playerEHandle = player.GetEncodedEHandle() - array<entity> players = GetPlayerArray() - foreach ( ent in players ) - { - if ( ent != player ) - Remote_CallFunction_Replay( ent, "ServerCallback_PlayerConnectedOrDisconnected", playerEHandle, state ) - } -} - -function NotifyClientsOfTeamChange( entity player, int oldTeam, int newTeam ) -{ - int playerEHandle = player.GetEncodedEHandle() - array<entity> players = GetPlayerArray() - foreach ( ent in players ) - { - //if ( ent != player ) - Remote_CallFunction_Replay( ent, "ServerCallback_PlayerChangedTeams", playerEHandle, oldTeam, newTeam ) - } -} - - -bool function IsValidNPCTarget( entity ent ) -{ - switch ( ent.GetClassName() ) - { - case "npc_marvin": - case "npc_soldier": - case "npc_spectre": - case "npc_stalker": - case "npc_super_spectre": - case "npc_prowler": - case "npc_drone": - case "npc_titan": - case "npc_turret_sentry": - case "npc_turret_mega": - case "npc_dropship": - return true - } - - return false -} - -int function CodeCallback_GetWeaponDamageSourceId( entity weapon ) -{ - string classname = weapon.GetWeaponClassName() - - #if DEV - if ( ("devWeapons" in level) && classname in level.devWeapons ) - return 0 - - #endif - //Filter out abilities for now - if ( !(classname in eDamageSourceId) ) - return damagedef_unknown - - //Assert( classname in getconsttable().eDamageSourceId, classname + " not added to eDamageSourceId enum" ) - int damageSourceInt = eDamageSourceId[ classname ] - return damageSourceInt -} - - - - -function TriggerHurtSetup() -{ - file.hurtTriggers.extend( GetEntArrayByClass_Expensive( "trigger_hurt" ) ) - foreach( trigger in file.hurtTriggers ) - { - trigger.ConnectOutput( "OnStartTouch", TriggerHurtEnter ) - } -} - -void function TriggerHurtEnter( entity trigger, entity ent, entity caller, var value ) -{ - if ( ent.e.destroyTriggerHurt ) - ent.Destroy() -} - -#if MP -table< entity, table< entity, bool > > oob_triggerEntPairs - -void function SetupOutOfBoundsTrigger( entity trigger ) -{ - if ( !(trigger in oob_triggerEntPairs) ) - oob_triggerEntPairs[trigger] <- {} -} -#endif - -function OutOfBoundsSetup() -{ - file.outOfBoundsTriggers.extend( GetEntArrayByClass_Expensive( "trigger_out_of_bounds" ) ) - foreach( trigger in file.outOfBoundsTriggers ) - { - #if MP - SetupOutOfBoundsTrigger( trigger ) - trigger.ConnectOutput( "OnStartTouch", EntityEnterOutOfBoundsTrig ) - trigger.ConnectOutput( "OnEndTouch", EntityLeaveOutOfBoundsTrig ) - #else - trigger.ConnectOutput( "OnStartTouch", EntityOutOfBounds ) - trigger.ConnectOutput( "OnEndTouch", EntityBackInBounds ) - #endif - } - - AddCallback_GameStateEnter( eGameState.Postmatch, OutOfBoundsDisable ) -} - -void function OutOfBoundsDisable() -{ - foreach( trigger in file.outOfBoundsTriggers ) - { - #if MP - foreach ( ent, val in oob_triggerEntPairs[trigger] ) - oob_triggerEntPairs[trigger][ent] = false - trigger.DisconnectOutput( "OnStartTouch", EntityEnterOutOfBoundsTrig ) - trigger.DisconnectOutput( "OnEndTouch", EntityLeaveOutOfBoundsTrig ) - #else - trigger.DisconnectOutput( "OnStartTouch", EntityOutOfBounds ) - trigger.DisconnectOutput( "OnEndTouch", EntityBackInBounds ) - #endif - } -} - -bool function IsPointOutOfBounds( vector point ) -{ - foreach ( trigger in file.outOfBoundsTriggers ) - { - if ( trigger.ContainsPoint( point ) ) - return true - } - return false -} - -#if MP -void function EntityEnterOutOfBoundsTrig( entity trigger, entity ent, entity caller, var value ) -{ - if ( !IsValid( ent ) || !ent.IsPlayer() ) - { - EntityOutOfBounds( trigger, ent, null, null ) - return - } - - if ( !(ent in oob_triggerEntPairs[trigger]) ) - { - oob_triggerEntPairs[trigger][ent] <- true - thread EntityCheckOutOfBoundsThread( trigger, ent ) - } - else - { - oob_triggerEntPairs[trigger][ent] = true - // thread is already running - } -} - -void function EntityLeaveOutOfBoundsTrig( entity trigger, entity ent, entity caller, var value ) -{ - if ( !(ent in oob_triggerEntPairs[trigger]) ) - { - EntityBackInBounds( trigger, ent, null, null ) - return - } - - oob_triggerEntPairs[trigger][ent] = false // tell thread to stop -} - -bool function TriggerIsTouchingPlayerHullAtPoint( entity player, entity trigger, float triggerminz, vector pos, float radius ) -{ - if ( trigger.GetClassName() == "trigger_cylinder" ) - { - array<entity> touchingEnts = trigger.GetTouchingEntities() - return touchingEnts.contains( player ) - } - else - { - return BrushTriggerIsTouchingPlayerHullAtPoint( trigger, triggerminz, pos, radius ) - } - - unreachable -} - -bool function BrushTriggerIsTouchingPlayerHullAtPoint( entity trigger, float triggerminz, vector pos, float radius ) -{ - if ( pos.z < triggerminz ) - return false - - radius *= 1.0824 // expand by 1/cos(22.5) so that an octagon circumscribes the circle - - if ( trigger.ContainsPoint( pos ) || - trigger.ContainsPoint( pos + <radius,0,0> ) || - trigger.ContainsPoint( pos + < -radius,0,0> ) || - trigger.ContainsPoint( pos + <0,radius,0> ) || - trigger.ContainsPoint( pos + <0,-radius,0> ) ) - return true - - float radius45 = radius * 0.7071 - - if ( trigger.ContainsPoint( pos + <radius45,radius45,0> ) || - trigger.ContainsPoint( pos + < -radius45,-radius45,0> ) || - trigger.ContainsPoint( pos + <radius45,-radius45,0> ) || - trigger.ContainsPoint( pos + < -radius45,radius45,0> ) ) - return true - - return false -} - -void function EntityCheckOutOfBoundsThread( entity trigger, entity ent ) -{ - float minz = trigger.GetOrigin().z + trigger.GetBoundingMins().z - float radius = ent.GetBoundingMaxs().x - - bool wasTouching = false - for ( ;; ) - { - wait 0.099 - - if ( !IsValid( ent ) ) - break - - if ( !oob_triggerEntPairs[trigger][ent] ) - break - - bool isTouching - if ( ent.IsOnGround() ) - { - if ( ent.IsWallRunning() && !ent.IsWallHanging() ) - { - isTouching = TriggerIsTouchingPlayerHullAtPoint( ent, trigger, minz, ent.GetOrigin() + <0,0,10>, radius ) - } - else - { - isTouching = true - } - } - else - { - vector startpos = ent.GetOrigin() - vector endpos = startpos - endpos.z -= 2048 - - TraceResults result = TraceHull( startpos, endpos, ent.GetBoundingMins(), ent.GetBoundingMaxs(), ent, TRACE_MASK_PLAYERSOLID, TRACE_COLLISION_GROUP_PLAYER ) - if ( result.startSolid || result.fraction >= 1 || TriggerIsTouchingPlayerHullAtPoint( ent, trigger, minz, result.endPos + <0,0,40>, radius ) ) - { - //DebugDrawLine( startpos, result.endPos, 255,255,255, true, 3.0 ) - isTouching = true - } - else - { - //DebugDrawLine( startpos, result.endPos, 255,0,0, true, 3.0 ) - isTouching = false - } - } - - if ( isTouching == wasTouching ) - continue - - wasTouching = isTouching - if ( isTouching ) - { - EntityOutOfBounds( trigger, ent, null, null ) - } - else - { - EntityBackInBounds( trigger, ent, null, null ) - } - } - - if ( wasTouching ) - { - EntityBackInBounds( trigger, ent, null, null ) - } - - delete oob_triggerEntPairs[trigger][ent] -} -#endif - -void function EntityOutOfBounds( entity trigger, entity ent, entity caller, var value ) -{ - //printt( "ENTITY", ent, "IS OUT OF BOUNDS ON TRIGGER", trigger ) - - if ( ent.e.destroyOutOfBounds ) - ent.Destroy() - - if ( !IsValidOutOfBoundsEntity( ent, trigger ) ) - return - - //printt( "Valid Out OfBounds Entity, EntityOutOfBounds" ) - - if ( !(ent in file.outOfBoundsTable) ) //Note that we never remove the ent from the table after adding it - { - OutOfBoundsDataStruct initialDataStruct - initialDataStruct.timeBackInBound = max( 0, Time() - OUT_OF_BOUNDS_DECAY_TIME ) - - ManageAddEntToOutOfBoundsTable( ent, initialDataStruct ) - } - - OutOfBoundsDataStruct dataStruct = file.outOfBoundsTable[ ent ] - - dataStruct.outOfBoundsTriggersTouched++ - - Assert( dataStruct.outOfBoundsTriggersTouched > 0 ) - - // Not already touching another trigger - if ( dataStruct.outOfBoundsTriggersTouched == 1 ) - { - float decayTime = max( 0, Time() - dataStruct.timeBackInBound - OUT_OF_BOUNDS_DECAY_DELAY ) - float outOfBoundsTimeRegained = decayTime * ( OUT_OF_BOUNDS_TIME_LIMIT / OUT_OF_BOUNDS_DECAY_TIME ) - float deadTime = clamp( dataStruct.timeLeftBeforeDyingFromOutOfBounds + outOfBoundsTimeRegained, 0.0, OUT_OF_BOUNDS_TIME_LIMIT ) - - //printt( "Decay Time: " + decayTime + ", outOfBoundsTimeRegained:" + outOfBoundsTimeRegained + ", timeLeftBeforeDyingFromOutOfBounds: " + dataStruct.timeLeftBeforeDyingFromOutOfBounds + ", deadTime: " + deadTime ) - - dataStruct.timeLeftBeforeDyingFromOutOfBounds = deadTime - - ent.SetOutOfBoundsDeadTime( Time() + deadTime ) - - thread KillEntityOutOfBounds( ent, trigger ) - } - - //printt( "ent.GetOutOfBoundsDeadTime():", ent.GetOutOfBoundsDeadTime() ) -} - -bool function EntityIsOutOfBounds( entity ent ) -{ - if ( !( ent in file.outOfBoundsTable ) ) - return false - return file.outOfBoundsTable[ ent ].outOfBoundsTriggersTouched > 0 -} - -void function EntityBackInBounds( entity trigger, entity ent, entity caller, var value ) -{ - //printt( "ENTITY", ent, "IS BACK IN BOUNDS OF TRIGGER", trigger ) - - if ( !IsValidOutOfBoundsEntity( ent, trigger ) ) - return - - //printt( "Valid Out OfBounds Entity, EntityBackInBounds" ) - - if ( !(ent in file.outOfBoundsTable) ) //Can go back in bounds even though we went out of bounds as an invalid ent, e.g. in a dropship - { - OutOfBoundsDataStruct initialDataStruct - ManageAddEntToOutOfBoundsTable( ent, initialDataStruct ) - - ent.SetOutOfBoundsDeadTime( 0.0 ) - ent.Signal( "BackInBounds" ) - - return - } - else - { - OutOfBoundsDataStruct dataStruct = file.outOfBoundsTable[ ent ] - - dataStruct.outOfBoundsTriggersTouched-- - if ( dataStruct.outOfBoundsTriggersTouched < 0 ) //You can exit from bounds while being an invalid ent from out of bounds on the way in, e.g. during dropship anims, etc - dataStruct.outOfBoundsTriggersTouched = 0 - - if ( dataStruct.outOfBoundsTriggersTouched == 0 ) - { - dataStruct.timeBackInBound = Time() - dataStruct.timeLeftBeforeDyingFromOutOfBounds = max( 0, ent.GetOutOfBoundsDeadTime() - Time() ) - ent.SetOutOfBoundsDeadTime( 0.0 ) - ent.Signal( "BackInBounds" ) - return - } - } -} - -void function KillEntityOutOfBounds( entity ent, entity trigger ) -{ - if ( GetGameState() < eGameState.Playing ) - return - - Assert( ent.GetOutOfBoundsDeadTime() != 0 ) - Assert( Time() <= ent.GetOutOfBoundsDeadTime() ) - - ent.EndSignal( "OnDeath" ) - ent.Signal( "OutOfBounds" ) - ent.EndSignal( "OutOfBounds" ) - ent.EndSignal( "BackInBounds" ) - - OnThreadEnd( - function() : ( ent ) - { - if ( IsValid( ent ) && !IsAlive( ent ) ) - { - file.outOfBoundsTable[ ent ].outOfBoundsTriggersTouched = 0 - ent.SetOutOfBoundsDeadTime( 0 ) - } - } - ) - - wait ent.GetOutOfBoundsDeadTime() - Time() - - if ( !IsValidOutOfBoundsEntity( ent, trigger ) ) - return - - if ( ent.GetOutOfBoundsDeadTime() == 0 ) - return - - ent.Die( svGlobal.worldspawn, svGlobal.worldspawn, { scriptType = DF_INSTANT, damageSourceId = eDamageSourceId.outOfBounds } ) -} - -bool function IsValidOutOfBoundsEntity( entity ent, entity trigger ) -{ - if ( !IsValid( ent ) ) - return false - - if ( !IsAlive( ent ) ) - return false - - int triggerTeam = expect int( trigger.kv.teamnumber.tointeger() ) - - Assert( triggerTeam >= 0 ) - - if ( triggerTeam != 0 && ent.GetTeam() != triggerTeam ) - return false - - // Temp hack for tday intro, might not keep this - if ( "disableOutOfBounds" in level && level.disableOutOfBounds == true ) - return false - - if ( ent.IsPlayer() ) - { - if ( ent.IsNoclipping() && !ent.Anim_IsActive() ) //Need to check for Anim_IsActive because PlayAnim() calls will set IsNoclipping() to true. This caused a bug with ejecting out of a OutOfBounds trigger - return false - - entity parentEnt = ent.GetParent() - if ( IsValid( parentEnt ) && IsDropship( parentEnt ) ) - return false - - return true - } - - if ( ent.IsNPC() && ent.IsTitan() ) - return true - - return false -} - -void function OnTitanBecomesPilot_OutOfBoundsCheck( entity pilot, entity npc_titan ) -{ - if ( pilot.GetOutOfBoundsDeadTime() == 0 ) - return - - npc_titan.SetOrigin( npc_titan.GetOrigin() ) //Kinda a hack to force redetection of the Titan touching the out of bounds trigger -} - -void function ManageAddEntToOutOfBoundsTable( entity ent, OutOfBoundsDataStruct dataStruct ) //Might be overkill, but: suggested by Haggerty to avoid leak of constantly adding ents to the file table without removing them -{ - //First clean up dead references in table - table< entity, OutOfBoundsDataStruct> tempTable = clone file.outOfBoundsTable - - foreach( ent, dataStruct in tempTable ) - { - if ( !IsValid( ent ) ) - { - delete file.outOfBoundsTable[ ent ] - } - } - - //Now add the new ent - - file.outOfBoundsTable[ ent ] <- dataStruct -} - -bool function PlayerCanSpawn( entity player ) -{ - if ( IsAlive( player ) ) - return false - - if ( player.isSpawning ) - return false - - return true -} - -function SetTitanAvailable( entity player ) -{ - Assert( player.entindex() < 32 ) - int shiftIndex = player.entindex() - 1 - int elimMask = (1 << shiftIndex) - - level.nv.titanAvailableBits = level.nv.titanAvailableBits | elimMask - - #if MP - PIN_PlayerAbilityReady( player, "titanfall" ) - #endif -} - -function ClearTitanAvailable( entity player ) -{ - Assert( player.entindex() < 32 ) - int shiftIndex = player.entindex() - 1 - int elimMask = (1 << shiftIndex) - - level.nv.titanAvailableBits = level.nv.titanAvailableBits & (~elimMask) -} - - - -function SetRespawnAvailable( entity player ) -{ - Assert( player.entindex() < 32 ) - int shiftIndex = player.entindex() - 1 - int elimMask = (1 << shiftIndex) - - level.nv.respawnAvailableBits = level.nv.respawnAvailableBits | elimMask -} - - -function ClearRespawnAvailable( entity player ) -{ - Assert( player.entindex() < 32 ) - int shiftIndex = player.entindex() - 1 - int elimMask = (1 << shiftIndex) - - level.nv.respawnAvailableBits = level.nv.respawnAvailableBits & (~elimMask) -} - - -void function SetPlayerEliminated( entity player ) -{ - player.SetPlayerGameStat( PGS_ELIMINATED, 1 ) -} - -void function ClearPlayerEliminated( entity player ) -{ - player.SetPlayerGameStat( PGS_ELIMINATED, 0 ) -} - -bool function IsPlayerEliminated( entity player ) -{ - return (player.GetPlayerGameStat( PGS_ELIMINATED ) > 0) -} - -bool function IsTeamEliminated( int team ) -{ - array<entity> players = GetPlayerArrayOfTeam( team ) - - foreach ( player in players ) - { - if ( IsPlayerEliminated( player ) != true ) - return false - } - - return true -} - -// Clears all scoreboard data for the player to make sure we never use old data -void function ClearPostGameScoreboardData( entity player ) -{ - if ( !IsValid( player ) || !player.IsPlayer() ) - return - - player.SetPersistentVar( "isPostGameScoreboardValid", false ) - player.SetPersistentVar( "isFDPostGameScoreboardValid", false ) -} - -bool function ShouldShowLossProtectionOnEOG( entity player ) -{ - if ( player.p.hasMatchLossProtection != true ) - return false - - if ( player.GetTeam() == GetWinningTeam() ) - return false - - if ( IsPrivateMatch() ) - return false - - return true -} - -bool function GameModeRemove( entity ent ) -{ - string gameMode = GameRules_GetGameMode() - switch ( gameMode ) - { - // These game modes have checkboxes in leveled - case LAST_TITAN_STANDING: - case TEAM_DEATHMATCH: - case ATTRITION: - case CAPTURE_POINT: - case CAPTURE_THE_FLAG: - case FORT_WAR: - case FFA: - case FD: - break - - // These game modes use tdm spawns - case PILOT_SKIRMISH: - case WINGMAN_PILOT_SKIRMISH: - case MARKED_FOR_DEATH_PRO: - case MARKED_FOR_DEATH: - case T_DAY: - case AI_TDM: - case BOMB: - case HARDCORE_TDM: - case COLISEUM: - case HUNTED: - case DON: - case TITAN_BRAWL: - case SPEEDBALL: - gameMode = TEAM_DEATHMATCH - break - - case RAID: - case ATCOOP: - case CONQUEST: - case PVE_SANDBOX: - gameMode = ATTRITION - break - - case LTS_BOMB: - case WINGMAN_LAST_TITAN_STANDING: - gameMode = LAST_TITAN_STANDING - break - - case FREE_AGENCY: - gameMode = FFA - break - - default: - // If a game mode is not handled in here, spawnpoints won't have checkboxes that correspond to it, so all spawnpoints will be used in that mode, which is probably bad. - Assert( false, "Game mode " + gameMode + " not handled in GameModeRemove()" ) - } - - AT_CollisionCleanup( ent ) - - string gamemodeKey = "gamemode_" + gameMode - if ( ent.HasKey( gamemodeKey ) && (ent.kv[gamemodeKey] == "0" || ent.kv[gamemodeKey] == "") ) - { - // printt( "Removing ent " + ent.GetClassName() + " with " + gamemodeKey + " = \"" + ent.kv[gamemodeKey] + "\" at " + ent.GetOrigin() ) - ent.Destroy() - return true - } - //printt( "keeping ent", ent.GetClassName() ) - - return false -} - -void function AT_CollisionCleanup( entity spawnPoint ) -{ - if ( spawnPoint.GetScriptName() == "at_mega_turret" ) - { - if ( spawnPoint.GetLinkEnt() != null ) // assuming this is func_brush_navmesh_separator - { - entity brush = spawnPoint.GetLinkEnt() - brush.NotSolid() - } - } -} - - -void function EntityFire( entity ent, string fire ) -{ - ent.Fire( fire ) -} - -void function EntityFireDelayed( entity ent, string fire, string parm, float delay ) -{ - ent.Fire( fire, parm, delay ) -} - -#if MP -void function AddOutOfBoundsTriggerWithParams( vector org, float radius = 250.0, float height = 250.0 ) -{ - entity trigger = CreateEntity( "trigger_cylinder" ) - trigger.SetRadius( radius ) - trigger.SetAboveHeight( height ) //Still not quite a sphere, will see if close enough - trigger.SetBelowHeight( height ) - trigger.SetOrigin( org ) - DispatchSpawn( trigger ) - SetupOutOfBoundsTrigger( trigger ) - trigger.SetEnterCallback( OnOOBTriggerEnter ) - trigger.SetLeaveCallback( OnOOBTriggerLeave ) -} - -void function OnOOBTriggerEnter( entity trigger, entity ent ) -{ - EntityEnterOutOfBoundsTrig( trigger, ent, null, 0 ) -} - -void function OnOOBTriggerLeave( entity trigger, entity ent ) -{ - EntityLeaveOutOfBoundsTrig( trigger, ent, null, 0 ) -} -#endif
\ No newline at end of file |