aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut
diff options
context:
space:
mode:
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/mp/_base_gametype.gnut2179
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