untyped globalize_all_functions const DEV_DRAWALLTRIGGERS = 0 global const CHARGE_TOOL = "sp_weapon_arc_tool" global const TRIG_FLAG_NONE = 0 global const TRIG_FLAG_PLAYERONLY = 0x0001 global const TRIG_FLAG_NPCONLY = 0x0002 global const TRIG_FLAG_NOCONTEXTBUSY = 0x0004 global const TRIG_FLAG_ONCE = 0x0008 global const TRIG_FLAG_EXCLUSIVE = 0x0010 // can only be triggered by entities passed in at creation global const TRIG_FLAG_DEVDRAW = 0x0020 global const TRIG_FLAG_START_DISABLED = 0x0040 global const TRIG_FLAG_NO_PHASE_SHIFT = 0x0080 global const float MAP_EXTENTS = 128*128 /* const TRIG_FLAG_ = 0x0080 const TRIG_FLAG_ = 0x0100*/ global const TRIGGER_INTERNAL_SIGNAL = "OnTrigger" global const CALCULATE_SEQUENCE_BLEND_TIME = -1.0 global struct ArrayDistanceEntry { float distanceSqr entity ent vector origin } global struct GravityLandData { array points TraceResults& traceResults float elapsedTime } global struct FirstPersonSequenceStruct { string firstPersonAnim = "" string thirdPersonAnim = "" string firstPersonAnimIdle = "" string thirdPersonAnimIdle = "" string relativeAnim = "" string attachment = "" bool teleport = false bool noParent = false float blendTime = CALCULATE_SEQUENCE_BLEND_TIME float firstPersonBlendOutTime = -1.0 bool noViewLerp = false bool hideProxy = false void functionref( entity ) viewConeFunction = null vector ornull origin = null vector ornull angles = null bool enablePlanting = false float setInitialTime = 0.0 //set the starting point of the animation in seconds bool useAnimatedRefAttachment = false //Position entity using ref every frame instead of using root motion bool renderWithViewModels = false bool gravity = false // force gravity command on sequence bool playerPushable = false array< string > thirdPersonCameraAttachments = [] bool thirdPersonCameraVisibilityChecks = false entity thirdPersonCameraEntity = null bool snapPlayerFeetToEyes = true } global struct FrontRightDotProductsStruct { float forwardDot = 0.0 float rightDot = 0.0 } global struct RaySphereIntersectStruct { bool result float enterFrac float leaveFrac } void function Utility_Shared_Init() { RegisterSignal( TRIGGER_INTERNAL_SIGNAL ) RegisterSignal( "devForcedWin" ) #document( "IsAlive", "Returns true if the given ent is not null, and is alive." ) #document( "ArrayWithin", "Remove ents from array that are out of range" ) } #if DEV // short cut for the console // script gp()[0].Die( gp()[1] ) array function gp() { return GetPlayerArray() } #endif void function InitWeaponScripts() { SmartAmmo_Init() // WEAPON SCRIPTS ArcCannon_Init() Grenade_FileInit() Vortex_Init() // #if SERVER // PrecacheProjectileEntity( "grenade_frag" ) // PrecacheProjectileEntity( "crossbow_bolt" ) // #endif MpWeaponDroneBeam_Init() MpWeaponDroneRocket_Init() MpWeaponDronePlasma_Init() MpWeaponTurretPlasma_Init() MpWeaponTurretLaser_Init() MpWeaponSuperSpectre_Init() MpWeaponGunshipLauncher_Init() MpWeaponFragDrone_Init() MpAbilityShifter_Init() MpTitanabilityBubbleShield_Init() MpTitanabilityAmpedWall_Init() MpTitanabilityFusionCore_Init() MpTitanweapon40mm_Init() MpTitanWeaponpredatorcannon_Init() MpTitanweaponRocketeetRocketStream_Init() MpTitanweaponMeteor_Init() MpTitanWeapon_SniperInit() MpTitanweaponVortexShield_Init() MpTitanweaponXo16_Init() MpWeaponDefender_Init() MpWeaponDmr_Init() MpWeaponProximityMine_Init() MpWeaponRocketLauncher_Init() MpWeaponNPCRocketLauncher_Init() MpWeaponSatchel_Init() MpWeaponSmartPistol_Init() MpWeaponSniper_Init() MpWeaponLSTAR_Init() MpTitanWeaponParticleAccelerator_Init() MpWeaponMegaTurret_Init() MpWeaponZipline_Init() SpWeaponHoldBeam_Init() MpTitanweaponArcBall_Init() MpWeaponDeployableCover_Init() MpTitanAbilityBasicBlock_Init() MpTitanAbilityLaserTrip_Init() MpTitanWeaponArcWave_Init() MpTitanWeaponFlameWave_Init() MpWeaponAlternatorSMG_Init() MpWeaponGreandeElectricSmoke_Init() MpWeaponGrenadeGravity_Init() MpWeaponDeployableCloakfield_Init() MpWeaponTether_Init() MpWeaponTripWire_Init() MpTitanAbilitySmartCore_Init() MpTitanAbilitySlowTrap_Init() MpTitanAbilityPowerShot_Init() MpTitanAbilityAmmoSwap_Init() MpTitanAbilityRocketeerAmmoSwap_Init() MpTitanAbilityHeatShield_Init() SonarGrenade_Init() MpTitanAbilityGunShield_Init() MpTitanWeaponLaserLite_Init() MpTitanWeaponSword_Init() MpTitanAbilityHover_Init() MpTitanWeaponTrackerRockets_Init() MpTitanWeaponStunLaser_Init() MpTitanWeaponShoulderRockets_Init() MpTitanAbilitySmoke_Init() #if MP MpWeaponArcTrap_Init() #endif #if SERVER BallLightning_Init() #endif } float function GetCurrentPlaylistVarFloat( string val, float useVal ) { var result = GetCurrentPlaylistVarOrUseValue( val, useVal + "" ) if ( result == null || result == "" ) return 0.0 return float( result ) } void function SetSkinForTeam( entity ent, int team ) { if ( team == TEAM_IMC ) ent.SetSkin( 0 ) else if ( team == TEAM_MILITIA ) ent.SetSkin( 1 ) } void function TableDump( table Table, int depth = 0 ) { if ( depth > 4 ) return foreach ( k, v in Table ) { printl( "Key: " + k + " Value: " + v ) if ( type( v ) == "table" && depth ) TableDump( expect table( v ), depth + 1 ) } } /*entity function GetVortexWeapon( entity player ) { for ( int weaponIndex = 0; weaponIndex < 2; weaponIndex++ ) { entity weapon = player.GetOffhandWeapon( weaponIndex ) if ( !IsValid( weapon ) ) continue if ( weapon.GetWeaponClassName() != "mp_titanweapon_vortex_shield" ) continue return weapon } Assert( false, "Vortex weapon not found!" ) unreachable }*/ entity function GetClosest( array entArray, vector origin, float maxdist = -1.0 ) { Assert( entArray.len() > 0 ) entity bestEnt = entArray[ 0 ] float bestDistSqr = DistanceSqr( bestEnt.GetOrigin(), origin ) for ( int i = 1; i < entArray.len(); i++ ) { entity newEnt = entArray[ i ] float newDistSqr = LengthSqr( newEnt.GetOrigin() - origin ) if ( newDistSqr < bestDistSqr ) { bestEnt = newEnt bestDistSqr = newDistSqr } } if ( maxdist >= 0.0 ) { if ( bestDistSqr > maxdist * maxdist ) return null } return bestEnt } entity function GetClosest2D( array entArray, vector origin, float maxdist = -1.0 ) { Assert( entArray.len() > 0, "Empty array!" ) entity bestEnt = entArray[ 0 ] float bestDistSqr = DistanceSqr( bestEnt.GetOrigin(), origin ) for ( int i = 1; i < entArray.len(); i++ ) { entity newEnt = entArray[ i ] float newDistSqr = Length2DSqr( newEnt.GetOrigin() - origin ) if ( newDistSqr < bestDistSqr ) { bestEnt = newEnt bestDistSqr = newDistSqr } } if ( maxdist >= 0.0 ) { if ( bestDistSqr > maxdist * maxdist ) return null } return bestEnt } bool function GameModeHasCapturePoints() { #if CLIENT return clGlobal.hardpointStringIDs.len() > 0 #elseif SERVER return svGlobal.hardpointStringIDs.len() > 0 #endif } entity function GetFarthest( array entArray, vector origin ) { Assert( entArray.len() > 0, "Empty array!" ) entity bestEnt = entArray[0] float bestDistSqr = DistanceSqr( bestEnt.GetOrigin(), origin ) for ( int i = 1; i < entArray.len(); i++ ) { entity newEnt = entArray[ i ] float newDistSqr = DistanceSqr( newEnt.GetOrigin(), origin ) if ( newDistSqr > bestDistSqr ) { bestEnt = newEnt bestDistSqr = newDistSqr } } return bestEnt } int function GetClosestIndex( array Array, vector origin ) { Assert( Array.len() > 0 ) int index = 0 float distSqr = LengthSqr( Array[ index ].GetOrigin() - origin ) entity newEnt float newDistSqr for ( int i = 1; i < Array.len(); i++ ) { newEnt = Array[ i ] newDistSqr = LengthSqr( newEnt.GetOrigin() - origin ) if ( newDistSqr < distSqr ) { index = i distSqr = newDistSqr } } return index } // nothing in the game uses the format "Table.r/g/b/a"... wtf is the point of this function table function StringToColors( string colorString, string delimiter = " " ) { PerfStart( PerfIndexShared.StringToColors + SharedPerfIndexStart ) array tokens = split( colorString, delimiter ) Assert( tokens.len() >= 3 ) table Table = {} Table.r <- int( tokens[0] ) Table.g <- int( tokens[1] ) Table.b <- int( tokens[2] ) if ( tokens.len() == 4 ) Table.a <- int( tokens[3] ) else Table.a <- 255 PerfEnd( PerfIndexShared.StringToColors + SharedPerfIndexStart ) return Table } // TODO: Set return type to array when SetColor() accepts this type function ColorStringToArray( string colorString ) { array tokens = split( colorString, " " ) Assert( tokens.len() >= 3 && tokens.len() <= 4 ) array colorArray foreach ( token in tokens ) colorArray.append( int( token ) ) return colorArray } // Evaluate a generic order ( coefficientArray.len() - 1 ) polynomial // e.g. to evaluate (Ax + B), call EvaluatePolynomial(x, A, B) // Note that EvaluatePolynomial(x) returns 0 and // EvaluatePolynomial(x, A) returns A, which are technically correct // but perhaps not what you expect float function EvaluatePolynomial( float x, array coefficientArray ) { float sum = 0.0 for ( int i = 0; i < coefficientArray.len() - 1; ++i ) sum += coefficientArray[ i ] * pow( x, coefficientArray.len() -1 - i ) if ( coefficientArray.len() >= 1 ) sum += coefficientArray[ coefficientArray.len() - 1 ] return sum } void function WaitForever() { #if SERVER svGlobal.levelEnt.WaitSignal( "forever" ) #elseif CLIENT clGlobal.levelEnt.WaitSignal( "forever" ) #endif } #if SERVER bool function ShouldDoReplay( entity player, entity attacker, float replayTime, int methodOfDeath ) { if ( ShouldDoReplayIsForcedByCode() ) { print( "ShouldDoReplay(): Doing a replay because code forced it." ); return true } if ( GetCurrentPlaylistVarInt( "replay_disabled", 0 ) == 1 ) { print( "ShouldDoReplay(): Not doing a replay because 'replay_disabled' is enabled in the current playlist.\n" ); return false } switch( methodOfDeath ) { case eDamageSourceId.human_execution: case eDamageSourceId.titan_execution: { print( "ShouldDoReplay(): Not doing a replay because the player died from an execution.\n" ); return false } } if ( level.nv.replayDisabled ) { print( "ShouldDoReplay(): Not doing a replay because replays are disabled for the level.\n" ); return false } if ( Time() - player.p.connectTime <= replayTime ) //Bad things happen if we try to do a kill replay that lasts longer than the player entity existing on the server { print( "ShouldDoReplay(): Not doing a replay because the player is not old enough.\n" ); return false } if ( player == attacker ) { print( "ShouldDoReplay(): Not doing a replay because the attacker is the player.\n" ); return false } if ( player.IsBot() == true ) { print( "ShouldDoReplay(): Not doing a replay because the player is a bot.\n" ); return false } return AttackerShouldTriggerReplay( attacker ) } // Don't let things like killbrushes show replays bool function AttackerShouldTriggerReplay( entity attacker ) { if ( !IsValid( attacker ) ) { print( "AttackerShouldTriggerReplay(): Not doing a replay because the attacker is not valid.\n" ) return false } if ( attacker.IsPlayer() ) { print( "AttackerShouldTriggerReplay(): Doing a replay because the attacker is a player.\n" ) return true } if ( attacker.IsNPC() ) { print( "AttackerShouldTriggerReplay(): Doing a replay because the attacker is an NPC.\n" ) return true } print( "AttackerShouldTriggerReplay(): Not doing a replay by default.\n" ) return false } #endif // #if SERVER vector function RandomVec( float range ) { // could rewrite so it doesnt make a box of random. vector vec = Vector( 0, 0, 0 ) vec.x = RandomFloatRange( -range, range ) vec.y = RandomFloatRange( -range, range ) vec.z = RandomFloatRange( -range, range ) return vec } function ArrayValuesToTableKeys( arr ) { Assert( type( arr ) == "array", "Not an array" ) local resultTable = {} for ( int i = 0; i < arr.len(); ++ i) { resultTable[ arr[ i ] ] <- 1 } return resultTable } function TableKeysToArray( tab ) { Assert( type( tab ) == "table", "Not a table" ) local resultArray = [] resultArray.resize( tab.len() ) int currentArrayIndex = 0 foreach ( key, val in tab ) { resultArray[ currentArrayIndex ] = key ++currentArrayIndex } return resultArray } function TableRandom( Table ) { Assert( type( Table ) == "table", "Not a table" ) local Array = [] foreach ( entry, contents in Table ) { Array.append( contents ) } return Array.getrandom() } int function RandomWeightedIndex( array Array ) { int count = Array.len() Assert( count != 0, "Array is empty" ) int sum = int( ( count * ( count + 1 ) ) / 2.0 ) // ( n * ( n + 1 ) ) / 2 int randInt = RandomInt( sum ) for ( int i = 0 ; i < count ; i++ ) { int rangeForThisIndex = count - i if ( randInt < rangeForThisIndex ) return i randInt -= rangeForThisIndex } Assert( 0 ) unreachable } bool function IsValid_ThisFrame( entity ent ) { if ( ent == null ) return false return expect bool( ent.IsValidInternal() ) } bool function IsAlive( entity ent ) { if ( ent == null ) return false if ( !ent.IsValidInternal() ) return false return ent.IsEntAlive() } #if DEV && SERVER void function vduon() { PlayConversationToAll( "TitanReplacement" ) } void function playconvtest( string conv ) { entity player = GetPlayerArray()[0] array guys = GetAllSoldiers() if ( !guys.len() ) { printt( "No AI!!" ) return } entity guy = GetClosest( guys, player.GetOrigin() ) if ( conv in player.s.lastAIConversationTime ) delete player.s.lastAIConversationTime[ conv ] printt( "Play ai conversation " + conv ) PlaySquadConversationToAll( conv, guy ) } #endif //DEV void function FighterExplodes( entity ship ) { vector origin = ship.GetOrigin() vector angles = ship.GetAngles() EmitSoundAtPosition( TEAM_UNASSIGNED, origin, "AngelCity_Scr_RedeyeWeaponExplos" ) #if SERVER PlayFX( FX_HORNET_DEATH, origin ) #else int fxid = GetParticleSystemIndex( FX_HORNET_DEATH ) StartParticleEffectInWorld( fxid, origin, angles ) #endif } vector function PositionOffsetFromEnt( entity ent, float offsetX, float offsetY, float offsetZ ) { vector angles = ent.GetAngles() vector origin = ent.GetOrigin() origin += AnglesToForward( angles ) * offsetX origin += AnglesToRight( angles ) * offsetY origin += AnglesToUp( angles ) * offsetZ return origin } vector function PositionOffsetFromOriginAngles( vector origin, vector angles, float offsetX, float offsetY, float offsetZ ) { origin += AnglesToForward( angles ) * offsetX origin += AnglesToRight( angles ) * offsetY origin += AnglesToUp( angles ) * offsetZ return origin } bool function IsMenuLevel() { return IsLobby() } function Dump( package, depth = 0 ) { if ( depth > 6 ) return foreach ( k, v in package ) { for ( int i = 0; i < depth; i++ ) print( " ") if ( IsTable( package ) ) printl( "Key: " + k + " Value: " + v ) if ( IsArray( package ) ) printl( "Index: " + k + " Value: " + v ) if ( IsTable( v ) || IsArray( v ) ) Dump( v, depth + 1 ) } } bool function UseShortNPCTitles() { return GetCurrentPlaylistVarInt( "npc_short_titles", 0 ) ? true : false } string function GetShortNPCTitle( int team ) { return GetTeamName( team ) } bool function IsIMCOrMilitiaTeam( int team ) { return team == TEAM_MILITIA || team == TEAM_IMC } int function GetOtherTeam( int team ) { if ( team == TEAM_IMC ) return TEAM_MILITIA if ( team == TEAM_MILITIA ) return TEAM_IMC Assert( false, "Trying to GetOtherTeam() for team: " + team + " that is neither Militia nor IMC" ) unreachable } float function VectorDot_PlayerToOrigin( entity player, vector targetOrigin ) { vector playerEyePosition = player.EyePosition() vector vecToEnt = ( targetOrigin - playerEyePosition ) vecToEnt.Norm() // GetViewVector() only works on the player float dotVal = vecToEnt.Dot( player.GetViewVector() ) return dotVal } float function VectorDot_DirectionToOrigin( entity player, vector direction, vector targetOrigin ) { vector playerEyePosition = player.EyePosition() vector vecToEnt = ( targetOrigin - playerEyePosition ) vecToEnt.Norm() // GetViewVector() only works on the player float dotVal = DotProduct( vecToEnt, direction ) return dotVal } void function WaitUntilWithinDistance( entity player, entity titan, float dist ) { float distSqr = dist * dist for ( ;; ) { if ( !IsAlive( titan ) ) return if ( IsAlive( player ) ) { if ( DistanceSqr( player.GetOrigin(), titan.GetOrigin() ) <= distSqr ) return } wait 0.1 } } void function WaitUntilBeyondDistance( entity player, entity titan, float dist ) { float distSqr = dist * dist for ( ;; ) { if ( !IsAlive( titan ) ) return if ( IsAlive( player ) ) { if ( DistanceSqr( player.GetOrigin(), titan.GetOrigin() ) > distSqr ) return } wait 0.1 } } bool function IsModelViewer() { return GetMapName() == "mp_model_viewer" } //----------------------------------// // Tweening functions // // Pass in a fraction 0.0 - 1.0 // // Get a fraction back 0.0 - 1.0 // //----------------------------------// // simple linear tweening - no easing, no acceleration float function Tween_Linear( float frac ) { Assert( frac >= 0.0 && frac <= 1.0 ) return frac } // quadratic easing out - decelerating to zero velocity float function Tween_QuadEaseOut( float frac ) { Assert( frac >= 0.0 && frac <= 1.0 ) return -1.0 * frac*(frac-2) } // exponential easing out - decelerating to zero velocity float function Tween_ExpoEaseOut( float frac ) { Assert( frac >= 0.0 && frac <= 1.0 ) return -pow( 2.0, -10.0 * frac ) + 1.0 } float function Tween_ExpoEaseIn( float frac ) { Assert( frac >= 0.0 && frac <= 1.0 ) return pow( 2, 10 * ( frac - 1 ) ); } bool function LegalOrigin( vector origin ) { if ( fabs( origin.x ) > MAX_WORLD_COORD ) return false if ( fabs( origin.y ) > MAX_WORLD_COORD ) return false if ( fabs( origin.z ) > MAX_WORLD_COORD ) return false return true } vector function AnglesOnSurface( surfaceNormal, playerVelocity ) { playerVelocity.Norm() vector right = CrossProduct( playerVelocity, surfaceNormal ) vector forward = CrossProduct( surfaceNormal, right ) vector angles = VectorToAngles( forward ) angles.z = atan2( right.z, surfaceNormal.z ) * RAD_TO_DEG return angles } vector function ClampToWorldspace( vector origin ) { // temp solution for start positions that are outside the world bounds origin.x = clamp( origin.x, -MAX_WORLD_COORD, MAX_WORLD_COORD ) origin.y = clamp( origin.y, -MAX_WORLD_COORD, MAX_WORLD_COORD ) origin.z = clamp( origin.z, -MAX_WORLD_COORD, MAX_WORLD_COORD ) return origin } function UseReturnTrue( user, usee ) { return true } function ControlPanel_CanUseFunction( playerUser, controlPanel ) { expect entity( playerUser ) expect entity( controlPanel ) // Does a simple cone FOV check from the screen to the player's eyes int maxAngleToAxisAllowedDegrees = 60 vector playerEyePos = playerUser.EyePosition() int attachmentIndex = controlPanel.LookupAttachment( "PANEL_SCREEN_MIDDLE" ) Assert( attachmentIndex != 0 ) vector controlPanelScreenPosition = controlPanel.GetAttachmentOrigin( attachmentIndex ) vector controlPanelScreenAngles = controlPanel.GetAttachmentAngles( attachmentIndex ) vector controlPanelScreenForward = AnglesToForward( controlPanelScreenAngles ) vector screenToPlayerEyes = Normalize( playerEyePos - controlPanelScreenPosition ) return DotProduct( screenToPlayerEyes, controlPanelScreenForward ) > deg_cos( maxAngleToAxisAllowedDegrees ) } function SentryTurret_CanUseFunction( playerUser, sentryTurret ) { expect entity( playerUser ) expect entity( sentryTurret ) // Does a simple cone FOV check from the screen to the player's eyes int maxAngleToAxisAllowedDegrees = 90 vector playerEyePos = playerUser.EyePosition() int attachmentIndex = sentryTurret.LookupAttachment( "turret_player_use" ) Assert( attachmentIndex != 0 ) vector sentryTurretUsePosition = sentryTurret.GetAttachmentOrigin( attachmentIndex ) vector sentryTurretUseAngles = sentryTurret.GetAttachmentAngles( attachmentIndex ) vector sentryTurretUseForward = AnglesToForward( sentryTurretUseAngles ) vector useToPlayerEyes = Normalize( playerEyePos - sentryTurretUsePosition ) return DotProduct( useToPlayerEyes, sentryTurretUseForward ) > deg_cos( maxAngleToAxisAllowedDegrees ) } void function ArrayRemoveInvalid( array ents ) { for ( int i = ents.len() - 1; i >= 0; i-- ) { if ( !IsValid( ents[ i ] ) ) ents.remove( i ) } } bool function HasDamageStates( entity ent ) { if ( !IsValid( ent ) ) return false return ( "damageStateInfo" in ent.s ) } bool function HasHitData( entity ent ) { return ( "hasHitData" in ent.s && expect bool( ent.s.hasHitData ) ) } FrontRightDotProductsStruct function GetFrontRightDots( entity baseEnt, entity relativeEnt, string optionalTag = "" ) { if ( optionalTag != "" ) { int attachIndex = baseEnt.LookupAttachment( optionalTag ) vector origin = baseEnt.GetAttachmentOrigin( attachIndex ) vector angles = baseEnt.GetAttachmentAngles( attachIndex ) angles.x = 0 angles.z = 0 vector forward = AnglesToForward( angles ) vector right = AnglesToRight( angles ) vector targetOrg = relativeEnt.GetOrigin() vector vecToEnt = ( targetOrg - origin ) // printt( "vecToEnt ", vecToEnt ) vecToEnt.z = 0 vecToEnt.Norm() FrontRightDotProductsStruct result result.forwardDot = DotProduct( vecToEnt, forward ) result.rightDot = DotProduct( vecToEnt, right ) // red: forward for incoming ent //DebugDrawLine( origin, origin + vecToEnt * 150, 255, 0, 0, true, 5 ) // green: tag forward //DebugDrawLine( origin, origin + forward * 150, 0, 255, 0, true, 5 ) // blue: tag right //DebugDrawLine( origin, origin + right * 150, 0, 0, 255, true, 5 ) return result } vector targetOrg = relativeEnt.GetOrigin() vector origin = baseEnt.GetOrigin() vector vecToEnt = ( targetOrg - origin ) vecToEnt.Norm() FrontRightDotProductsStruct result result.forwardDot = vecToEnt.Dot( baseEnt.GetForwardVector() ) result.rightDot = vecToEnt.Dot( baseEnt.GetRightVector() ) return result } array function GetAllPointsOnBezier( array points, int numSegments, float debugDrawTime = 0.0 ) { Assert( points.len() >= 2 ) Assert( numSegments > 0 ) array curvePoints = [] // Debug draw the points used for the curve if ( debugDrawTime ) { for ( int i = 0; i < points.len() - 1; i++ ) DebugDrawLine( points[i], points[i + 1], 150, 150, 150, true, debugDrawTime ) } for ( int i = 0; i < numSegments; i++ ) { float t = ( i.tofloat() / ( numSegments.tofloat() - 1.0 ) ).tofloat() curvePoints.append( GetSinglePointOnBezier( points, t ) ) } return curvePoints } vector function GetSinglePointOnBezier( array points, float t ) { // evaluate a point on a bezier-curve. t goes from 0 to 1.0 array lastPoints = clone points for(;;) { array newPoints = [] for ( int i = 0; i < lastPoints.len() - 1; i++ ) newPoints.append( lastPoints[i] + ( lastPoints[i+1] - lastPoints[i] ) * t ) if ( newPoints.len() == 1 ) return newPoints[0] lastPoints = newPoints } unreachable } bool function GetDoomedState( entity ent ) { entity soul = ent.GetTitanSoul() if ( !IsValid( soul ) ) return false return soul.IsDoomed() } bool function TitanCoreInUse( entity player ) { Assert( player.IsTitan() ) if ( !IsAlive( player ) ) return false return Time() < SoulTitanCore_GetExpireTime( player.GetTitanSoul() ) } // Return float or null function GetTitanCoreTimeRemaining( entity player ) { if ( !player.IsTitan() ) return null entity soul = player.GetTitanSoul() if ( !soul ) return null return SoulTitanCore_GetExpireTime( soul ) - Time() } bool function CoreAvailableDuringDoomState() { return true } bool function HasAntiTitanWeapon( entity guy ) { foreach ( weapon in guy.GetMainWeapons() ) { if ( weapon.GetWeaponType() == WT_ANTITITAN ) return true } return false } float function GetTitanCoreActiveTime( entity player ) { entity weapon = player.GetOffhandWeapon( OFFHAND_EQUIPMENT ) if ( !IsValid( weapon ) ) { printt( "WARNING: tried to get core active time, but core weapon was invalid." ) printt( "titan is alive? " + IsAlive( player ) ) return 5.0 // default } return GetTitanCoreDurationFromWeapon( weapon ) } float function GetTitanCoreChargeTime( entity player ) { entity weapon = player.GetOffhandWeapon( OFFHAND_EQUIPMENT ) if ( !IsValid( weapon ) ) { printt( "WARNING: tried to get core charge time, but core weapon was invalid." ) printt( "titan is alive? " + IsAlive( player ) ) return 1.0 // default } return GetTitanCoreChargeTimeFromWeapon( weapon ) } float function GetTitanCoreChargeTimeFromWeapon( entity weapon ) { return expect float( weapon.GetWeaponInfoFileKeyField( "chargeup_time" ) ) } float function GetTitanCoreBuildTimeFromWeapon( entity weapon ) { return expect float( weapon.GetWeaponInfoFileKeyField( "core_build_time" ).tofloat() ) } float function GetTitanCoreDurationFromWeapon( entity weapon ) { float coreDuration = weapon.GetCoreDuration() entity player = weapon.GetWeaponOwner() if ( IsValid( player ) && player.IsPlayer() ) { if ( PlayerHasPassive( player, ePassives.PAS_MARATHON_CORE ) ) coreDuration *= TITAN_CORE_MARATHON_CORE_MULTIPLIER } return coreDuration } float function GetCoreBuildTime( entity titan ) { if ( titan.IsPlayer() ) titan = GetTitanFromPlayer( titan ) Assert( titan != null ) entity coreWeapon = titan.GetOffhandWeapon( OFFHAND_EQUIPMENT ) if ( !IsValid( coreWeapon ) ) { //printt( "WARNING: tried to set build timer, but core weapon was invalid." ) //printt( "titan is alive? " + IsAlive( titan ) ) return 200.0 // default } return GetTitanCoreBuildTimeFromWeapon( coreWeapon ) } string function GetCoreShortName( entity titan ) { entity coreWeapon = titan.GetOffhandWeapon( OFFHAND_EQUIPMENT ) if ( !IsValid( coreWeapon ) ) { printt( "WARNING: tried to get core name, but core weapon was invalid." ) printt( "titan is alive? " + IsAlive( titan ) ) return "#HUD_READY" } string name = expect string( coreWeapon.GetWeaponInfoFileKeyField( "shortprintname" ) ) return name } string ornull function GetCoreOSConversationName( entity titan, string event ) { entity coreWeapon = titan.GetOffhandWeapon( OFFHAND_EQUIPMENT ) if ( !IsValid( coreWeapon ) ) { printt( "WARNING: tried to get core sound for " + event + ", but core weapon was invalid." ) printt( "titan is alive? " + IsAlive( titan ) ) return null } var alias = coreWeapon.GetWeaponInfoFileKeyField( "dialog_" + event ) if ( alias == null ) return null expect string( alias ) return alias } entity function GetTitanFromPlayer( entity player ) { Assert( player.IsPlayer() ) if ( player.IsTitan() ) return player return player.GetPetTitan() } int function GetNuclearPayload( entity player ) { if ( !GetDoomedState( player ) ) return 0 int payload = 0 if ( PlayerHasPassive( player, ePassives.PAS_NUCLEAR_CORE ) ) payload += 2 if ( PlayerHasPassive( player, ePassives.PAS_BUILD_UP_NUCLEAR_CORE ) ) payload += 1 return payload } entity function GetCloak( entity ent ) { return GetOffhand( ent, "mp_ability_cloak" ) } entity function GetOffhand( entity ent, string classname ) { entity offhand = ent.GetOffhandWeapon( OFFHAND_LEFT ) if ( IsValid( offhand ) && offhand.GetWeaponClassName() == classname ) return offhand offhand = ent.GetOffhandWeapon( OFFHAND_RIGHT ) if ( IsValid( offhand ) && offhand.GetWeaponClassName() == classname ) return offhand return null } bool function IsCloaked( entity ent ) { return ent.IsCloaked( true ) //pass true to ignore flicker time - } float function TimeSpentInCurrentState() { return Time() - expect float( level.nv.gameStateChangeTime ) } float function DotToAngle( float dot ) { return acos( dot ) * RAD_TO_DEG } float function AngleToDot( float angle ) { return cos( angle * DEG_TO_RAD ) } int function GetGameState() { return expect int( GetServerVar( "gameState" ) ) } bool function GamePlaying() { return GetGameState() == eGameState.Playing } bool function GamePlayingOrSuddenDeath() { int gameState = GetGameState() return gameState == eGameState.Playing || gameState == eGameState.SuddenDeath } bool function IsOdd( int num ) { return ( num % 2 ) == 1 } bool function IsEven( int num ) { return !IsOdd( num ) } vector function VectorReflectionAcrossNormal( vector vec, vector normal ) { return ( vec - normal * ( 2 * DotProduct( vec, normal ) ) ) } // Return an array of entities ordered from farthest to closest to the specified origin array function ArrayFarthest( array entArray, vector origin ) { array allResults = ArrayDistanceResults( entArray, origin ) allResults.sort( DistanceCompareFarthest ) array returnEntities foreach ( result in allResults ) returnEntities.append( result.ent ) // the actual distances aren't returned return returnEntities } // Return an array of vectors ordered from closest to furthest from the specified origin array function ArrayFarthestVector( array vecArray, vector origin ) { array allResults = ArrayDistanceResultsVector( vecArray, origin ) allResults.sort( DistanceCompareFarthest ) array returnVecs foreach ( result in allResults ) returnVecs.append( result.origin ) return returnVecs } // Return an array of entities ordered from closest to furthest from the specified origin array function ArrayClosest( array entArray, vector origin ) { array allResults = ArrayDistanceResults( entArray, origin ) allResults.sort( DistanceCompareClosest ) array returnEntities foreach ( result in allResults ) returnEntities.append( result.ent ) return returnEntities } // Return an array of vectors ordered from closest to furthest from the specified origin array function ArrayClosestVector( array vecArray, vector origin ) { array allResults = ArrayDistanceResultsVector( vecArray, origin ) allResults.sort( DistanceCompareClosest ) array returnVecs foreach ( result in allResults ) returnVecs.append( result.origin ) return returnVecs } array function ArrayClosestWithinDistance( array entArray, vector origin, float maxDistance ) { array allResults = ArrayDistanceResults( entArray, origin ) float maxDistSq = maxDistance * maxDistance allResults.sort( DistanceCompareClosest ) array returnEntities foreach ( result in allResults ) { if ( result.distanceSqr > maxDistSq ) break returnEntities.append( result.ent ) } return returnEntities } array function ArrayClosestVectorWithinDistance( array vecArray, vector origin, float maxDistance ) { array allResults = ArrayDistanceResultsVector( vecArray, origin ) float maxDistSq = maxDistance * maxDistance allResults.sort( DistanceCompareClosest ) array returnVecs foreach ( result in allResults ) { if ( result.distanceSqr > maxDistSq ) break returnVecs.append( result.origin ) } return returnVecs } // Return an array of entities ordered from closest to furthest from the specified origin, ignoring z array function ArrayClosest2D( array entArray, vector origin ) { array allResults = ArrayDistance2DResults( entArray, origin ) allResults.sort( DistanceCompareClosest ) array returnEntities foreach ( result in allResults ) returnEntities.append( result.ent ) return returnEntities } // Return an array of entities ordered from closest to furthest from the specified origin, ignoring z array function ArrayClosest2DVector( array entArray, vector origin ) { array allResults = ArrayDistance2DResultsVector( entArray, origin ) allResults.sort( DistanceCompareClosest ) array returnVecs foreach ( result in allResults ) returnVecs.append( result.origin ) return returnVecs } array function ArrayClosest2DWithinDistance( array entArray, vector origin, float maxDistance ) { array allResults = ArrayDistance2DResults( entArray, origin ) float maxDistSq = maxDistance * maxDistance allResults.sort( DistanceCompareClosest ) array returnEntities foreach ( result in allResults ) { if ( result.distanceSqr > maxDistSq ) break returnEntities.append( result.ent ) } return returnEntities } // Return an array of entities ordered from closest to furthest from the specified origin, ignoring z array function ArrayClosest2DVectorWithinDistance( array entArray, vector origin, float maxDistance ) { array allResults = ArrayDistance2DResultsVector( entArray, origin ) float maxDistSq = maxDistance * maxDistance allResults.sort( DistanceCompareClosest ) array returnVecs foreach ( result in allResults ) { if ( result.distanceSqr > maxDistSq ) break returnVecs.append( result.origin ) } return returnVecs } bool function ArrayEntityWithinDistance( array entArray, vector origin, float distance ) { float distSq = distance * distance foreach( entity ent in entArray ) { if ( DistanceSqr( ent.GetOrigin(), origin ) <= distSq ) return true } return false } function TableRemove( Table, entry ) { Assert( typeof Table == "table" ) foreach ( index, tableEntry in Table ) { if ( tableEntry == entry ) { Table[ index ] = null } } } function TableInvert( Table ) { table invertedTable = {} foreach ( key, value in Table ) invertedTable[ value ] <- key return invertedTable } int function DistanceCompareClosest( ArrayDistanceEntry a, ArrayDistanceEntry b ) { if ( a.distanceSqr > b.distanceSqr ) return 1 else if ( a.distanceSqr < b.distanceSqr ) return -1 return 0; } int function DistanceCompareFarthest( ArrayDistanceEntry a, ArrayDistanceEntry b ) { if ( a.distanceSqr < b.distanceSqr ) return 1 else if ( a.distanceSqr > b.distanceSqr ) return -1 return 0; } array function ArrayDistanceResults( array entArray, vector origin ) { array allResults foreach ( ent in entArray ) { ArrayDistanceEntry entry vector entOrigin = ent.GetOrigin() if ( IsSpawner( ent ) ) { var spawnKVs = ent.GetSpawnEntityKeyValues() entOrigin = StringToVector( string( spawnKVs.origin ) ) } entry.distanceSqr = DistanceSqr( entOrigin, origin ) entry.ent = ent entry.origin = entOrigin allResults.append( entry ) } return allResults } array function ArrayDistanceResultsVector( array vecArray, vector origin ) { array allResults foreach ( vec in vecArray ) { ArrayDistanceEntry entry entry.distanceSqr = DistanceSqr( vec, origin ) entry.ent = null entry.origin = vec allResults.append( entry ) } return allResults } array function ArrayDistance2DResults( array entArray, vector origin ) { array allResults foreach ( ent in entArray ) { ArrayDistanceEntry entry vector entOrigin = ent.GetOrigin() entry.distanceSqr = Distance2DSqr( entOrigin, origin ) entry.ent = ent entry.origin = entOrigin allResults.append( entry ) } return allResults } array function ArrayDistance2DResultsVector( array vecArray, vector origin ) { array allResults foreach ( vec in vecArray ) { ArrayDistanceEntry entry entry.distanceSqr = Distance2DSqr( vec, origin ) entry.ent = null entry.origin = vec allResults.append( entry ) } return allResults } GravityLandData function GetGravityLandData( vector startPos, vector parentVelocity, vector objectVelocity, float timeLimit, bool bDrawPath = false, float bDrawPathDuration = 0.0, array pathColor = [ 255, 255, 0 ] ) { GravityLandData returnData Assert( timeLimit > 0 ) float MAX_TIME_ELAPSE = 6.0 float timeElapsePerTrace = 0.1 float sv_gravity = 750.0 float ent_gravity = 1.0 float gravityScale = 1.0 vector traceStart = startPos vector traceEnd = traceStart float traceFrac int traceCount = 0 objectVelocity += parentVelocity while( returnData.elapsedTime <= timeLimit ) { objectVelocity.z -= ( ent_gravity * sv_gravity * timeElapsePerTrace * gravityScale ) traceEnd += objectVelocity * timeElapsePerTrace returnData.points.append( traceEnd ) if ( bDrawPath ) DebugDrawLine( traceStart, traceEnd, pathColor[0], pathColor[1], pathColor[2], false, bDrawPathDuration ) traceFrac = TraceLineSimple( traceStart, traceEnd, null ) traceCount++ if ( traceFrac < 1.0 ) { returnData.traceResults = TraceLine( traceStart, traceEnd, null, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE ) return returnData } traceStart = traceEnd returnData.elapsedTime += timeElapsePerTrace } return returnData } float function GetPulseFrac( rate = 1, startTime = 0 ) { return (1 - cos( ( Time() - startTime ) * (rate * (2*PI)) )) / 2 } bool function IsPetTitan( titan ) { Assert( titan.IsTitan() ) return titan.GetTitanSoul().GetBossPlayer() != null } vector function StringToVector( string vecString, string delimiter = " " ) { array tokens = split( vecString, delimiter ) Assert( tokens.len() >= 3 ) return Vector( float( tokens[0] ), float( tokens[1] ), float( tokens[2] ) ) } float function GetShieldHealthFrac( entity ent ) { if ( !IsAlive( ent ) ) return 0.0 if ( HasSoul( ent ) ) { entity soul = ent.GetTitanSoul() if ( IsValid( soul ) ) ent = soul } int shieldHealth = ent.GetShieldHealth() int shieldMaxHealth = ent.GetShieldHealthMax() if ( shieldMaxHealth == 0 ) return 0.0 return float( shieldHealth ) / float( shieldMaxHealth ) } vector function HackGetDeltaToRef( vector origin, vector angles, entity ent, string anim ) { AnimRefPoint animStartPos = ent.Anim_GetStartForRefPoint( anim, origin, angles ) vector delta = origin - animStartPos.origin return origin + delta } vector function HackGetDeltaToRefOnPlane( vector origin, vector angles, entity ent, string anim, vector up ) { AnimRefPoint animStartPos = ent.Anim_GetStartForRefPoint( anim, origin, angles ) vector delta = origin - animStartPos.origin vector nDelta = Normalize( delta ) vector xProd = CrossProduct( nDelta, up ) vector G = CrossProduct( up, xProd ) vector planarDelta = G * DotProduct( delta, G ) vector P = origin + planarDelta // DebugDrawLine( origin + delta, origin, 255, 0, 0, true, 1.0 ) // DebugDrawLine( P, origin, 0,255, 100, true, 1.0 ) return P } TraceResults function GetViewTrace( entity ent ) { vector traceStart = ent.EyePosition() vector traceEnd = traceStart + (ent.GetPlayerOrNPCViewVector() * 56756) // longest possible trace given our map size limits array ignoreEnts = [ ent ] return TraceLine( traceStart, traceEnd, ignoreEnts, TRACE_MASK_SHOT, TRACE_COLLISION_GROUP_NONE ) } function GetModSourceID( modString ) { foreach ( name, id in getconsttable().eModSourceId ) { if ( string( name ) == modString ) return id } return null } void function ArrayRemoveDead( array entArray ) { for ( int i = entArray.len() - 1; i >= 0; i-- ) { if ( !IsAlive( entArray[ i ] ) ) entArray.remove( i ) } } array function GetSortedPlayers( IntFromEntityCompare compareFunc, int team ) { array players if ( team ) players = GetPlayerArrayOfTeam( team ) else players = GetPlayerArray() players.sort( compareFunc ) return players } // Sorts by kills and resolves ties in this order: fewest deaths, most titan kills, most assists int function CompareKills( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 aVal = a.GetPlayerGameStat( PGS_DEATHS ) bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal > bVal ) return 1 else if ( aVal < bVal ) return -1 aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 aVal = a.GetPlayerGameStat( PGS_ASSISTS ) bVal = b.GetPlayerGameStat( PGS_ASSISTS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } // Sorts by kills and resolves ties in this order: fewest deaths, most titan kills, most assists int function CompareAssaultScore( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_ASSAULT_SCORE ) int bVal = b.GetPlayerGameStat( PGS_ASSAULT_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareScore( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_SCORE ) int bVal = b.GetPlayerGameStat( PGS_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareAssault( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_ASSAULT_SCORE ) int bVal = b.GetPlayerGameStat( PGS_ASSAULT_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareDefense( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_DEFENSE_SCORE ) int bVal = b.GetPlayerGameStat( PGS_DEFENSE_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareLTS( entity a, entity b ) { int result = CompareTitanKills( a, b ) if ( result != 0 ) return result int aVal = a.GetPlayerGameStat( PGS_ASSAULT_SCORE ) int bVal = b.GetPlayerGameStat( PGS_ASSAULT_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareCP( entity a, entity b ) { // Capture Point sorting. Sort priority = assault + defense > pilot kills > titan kills > death { int aVal = a.GetPlayerGameStat( PGS_ASSAULT_SCORE ) int bVal = b.GetPlayerGameStat( PGS_ASSAULT_SCORE ) aVal += a.GetPlayerGameStat( PGS_DEFENSE_SCORE ) bVal += b.GetPlayerGameStat( PGS_DEFENSE_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 3) Pilot Kills { int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 3) Titan Kills { int aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) int bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 4) Deaths { int aVal = a.GetPlayerGameStat( PGS_DEATHS ) int bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 } return 0 } int function CompareCTF( entity a, entity b ) { // Capture the flag sorting. Sort priority = flag captures > flag returns > pilot kills > titan kills > death // 1) Flag Captures int result = CompareAssault( a, b ) if ( result != 0 ) return result // 2) Flag Returns result = CompareDefense( a, b ) if ( result != 0 ) return result // 3) Pilot Kills int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 3) Titan Kills aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 4) Deaths aVal = a.GetPlayerGameStat( PGS_DEATHS ) bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 return 0 } int function CompareSpeedball( entity a, entity b ) { // Capture the flag sorting. Sort priority = pilot kills > flag captures > death // 1) Pilot Kills int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 2) Flag Captures int result = CompareAssault( a, b ) if ( result != 0 ) return result // 3) Deaths aVal = a.GetPlayerGameStat( PGS_DEATHS ) bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 return 0 } int function CompareMFD( entity a, entity b ) { // 1) Marks Killed int result = CompareAssault( a, b ) if ( result != 0 ) return result // 2) Marks Outlasted result = CompareDefense( a, b ) if ( result != 0 ) return result // 3) Pilot Kills int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 4) Titan Kills aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 5) Deaths aVal = a.GetPlayerGameStat( PGS_DEATHS ) bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 return 0 } int function CompareScavenger( entity a, entity b ) { // 1) Ore Captured int result = CompareAssault( a, b ) if ( result != 0 ) return result // 2) Pilot Kills int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 3) Titan Kills aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 // 4) Deaths aVal = a.GetPlayerGameStat( PGS_DEATHS ) bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 return 0 } int function CompareFW( entity a, entity b ) { // Capture Point sorting. Sort priority = assault + defense > pilot kills > titan kills > death { int aVal = a.GetPlayerGameStat( PGS_ASSAULT_SCORE ) int bVal = b.GetPlayerGameStat( PGS_ASSAULT_SCORE ) aVal += a.GetPlayerGameStat( PGS_DEFENSE_SCORE ) bVal += b.GetPlayerGameStat( PGS_DEFENSE_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 3) Pilot Kills { int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 3) Titan Kills { int aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) int bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 4) Deaths { int aVal = a.GetPlayerGameStat( PGS_DEATHS ) int bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 } return 0 } int function CompareHunter( entity a, entity b ) { // Capture Point sorting. Sort priority = assault + defense > pilot kills > titan kills > death { int aVal = a.GetPlayerGameStat( PGS_ASSAULT_SCORE ) int bVal = b.GetPlayerGameStat( PGS_ASSAULT_SCORE ) aVal += a.GetPlayerGameStat( PGS_DEFENSE_SCORE ) bVal += b.GetPlayerGameStat( PGS_DEFENSE_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 3) Pilot Kills { int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 3) Titan Kills { int aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) int bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 } // 4) Deaths { int aVal = a.GetPlayerGameStat( PGS_DEATHS ) int bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal < bVal ) return -1 else if ( aVal > bVal ) return 1 } return 0 } // Sorts by kills, deaths and then cash int function CompareATCOOP( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_KILLS ) int bVal = b.GetPlayerGameStat( PGS_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 aVal = a.GetPlayerGameStat( PGS_DEATHS ) bVal = b.GetPlayerGameStat( PGS_DEATHS ) if ( aVal > bVal ) return 1 else if ( aVal < bVal ) return -1 aVal = a.GetPlayerGameStat( PGS_SCORE ) bVal = b.GetPlayerGameStat( PGS_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareFD( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_DETONATION_SCORE ) int bVal = b.GetPlayerGameStat( PGS_DETONATION_SCORE ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } int function CompareTitanKills( entity a, entity b ) { int aVal = a.GetPlayerGameStat( PGS_TITAN_KILLS ) int bVal = b.GetPlayerGameStat( PGS_TITAN_KILLS ) if ( aVal < bVal ) return 1 else if ( aVal > bVal ) return -1 return 0 } bool function TitanEjectIsDisabled() { return GetGlobalNetBool( "titanEjectEnabled" ) == false } bool function IsHitEffectiveVsTitan( entity victim, int damageType ) { Assert( victim.IsTitan() ) if ( victim.IsPlayer() ) { if ( PlayerHasPassive( victim, ePassives.PAS_BERSERKER ) ) return false } if ( !( damageType & DF_CRITICAL ) && ( damageType & DF_BULLET || damageType & DF_MAX_RANGE ) ) return false return true } bool function IsHitEffectiveVsNonTitan( entity victim, int damageType ) { if ( damageType & DF_BULLET || damageType & DF_MAX_RANGE ) return false; return true } bool function IsPilot( entity ent ) { if ( !IsValid( ent ) ) return false if ( !ent.IsPlayer() ) return false if ( ent.IsTitan() ) return false return true } bool function IsPilotDecoy( entity ent ) { if ( !IsValid( ent ) ) return false if ( ent.GetClassName() != "player_decoy" ) return false return true } string function HardpointIDToString( int id ) { array hardpointIDString = [ "a", "b", "c" ] Assert( id >= 0 && id < hardpointIDString.len() ) return hardpointIDString[ id ] } string function Dev_TeamIDToString( id ) { if ( id == TEAM_IMC ) return "IMC" if ( id == TEAM_MILITIA ) return "MIL" return "UNASSIGNED/UNKNOWN TEAM NAME" } array function ArrayWithin( array Array, vector origin, float maxDist ) { float maxDistSqr = maxDist * maxDist array resultArray = [] foreach ( ent in Array ) { float distSqr = DistanceSqr( origin, ent.GetOrigin() ) if ( distSqr <= maxDistSqr ) resultArray.append( ent ) } return resultArray } function GetTitanChassis( entity titan ) { if ( !("titanChassis" in titan.s ) ) { if ( HasSoul( titan ) ) { entity soul = titan.GetTitanSoul() titan.s.titanChassis <- GetSoulTitanSubClass( soul ) } else { return "Invalid Chassis" } } return titan.s.titanChassis } vector function ClampVectorToCube( vector vecStart, vector vec, vector cubeOrigin, float cubeSize ) { float halfCubeSize = cubeSize * 0.5 vector cubeMins = < -halfCubeSize, -halfCubeSize, -halfCubeSize > vector cubeMaxs = < halfCubeSize, halfCubeSize, halfCubeSize > return ClampVectorToBox( vecStart, vec, cubeOrigin, cubeMins, cubeMaxs ) } vector function ClampVectorToBox( vector vecStart, vector vec, vector cubeOrigin, vector cubeMins, vector cubeMaxs ) { float smallestClampScale = 1.0 vector vecEnd = vecStart + vec smallestClampScale = ClampVectorComponentToCubeMax( cubeOrigin.x, cubeMaxs.x, vecStart.x, vecEnd.x, vec.x, smallestClampScale ) smallestClampScale = ClampVectorComponentToCubeMax( cubeOrigin.y, cubeMaxs.y, vecStart.y, vecEnd.y, vec.y, smallestClampScale ) smallestClampScale = ClampVectorComponentToCubeMax( cubeOrigin.z, cubeMaxs.z, vecStart.z, vecEnd.z, vec.z, smallestClampScale ) smallestClampScale = ClampVectorComponentToCubeMin( cubeOrigin.x, cubeMins.x, vecStart.x, vecEnd.x, vec.x, smallestClampScale ) smallestClampScale = ClampVectorComponentToCubeMin( cubeOrigin.y, cubeMins.y, vecStart.y, vecEnd.y, vec.y, smallestClampScale ) smallestClampScale = ClampVectorComponentToCubeMin( cubeOrigin.z, cubeMins.z, vecStart.z, vecEnd.z, vec.z, smallestClampScale ) return vec * smallestClampScale } float function ClampVectorComponentToCubeMax( float cubeOrigin, float cubeSize, float vecStart, float vecEnd, float vec, float smallestClampScale ) { float max = cubeOrigin + cubeSize float clearance = fabs( vecStart - max ) if ( vecEnd > max ) { float scale = fabs( clearance / ( ( vecStart + vec ) - vecStart ) ) if ( scale > 0 && scale < smallestClampScale ) return scale } return smallestClampScale } float function ClampVectorComponentToCubeMin( float cubeOrigin, float cubeSize, float vecStart, float vecEnd, float vec, float smallestClampScale ) { float min = cubeOrigin - cubeSize float clearance = fabs( min - vecStart ) if ( vecEnd < min ) { float scale = fabs( clearance / ( ( vecStart + vec ) - vecStart ) ) if ( scale > 0 && scale < smallestClampScale ) return scale } return smallestClampScale } bool function PointInCapsule( vector vecBottom, vector vecTop, float radius, vector point ) { return GetDistanceFromLineSegment( vecBottom, vecTop, point ) <= radius } bool function PointInCylinder( vector vecBottom, vector vecTop, float radius, vector point ) { if ( GetDistanceFromLineSegment( vecBottom, vecTop, point ) > radius ) return false vector bottomVec = Normalize( vecTop - vecBottom ) vector pointToBottom = Normalize( point - vecBottom ) vector topVec = Normalize( vecBottom - vecTop ) vector pointToTop = Normalize( point - vecTop ) if ( DotProduct( bottomVec, pointToBottom ) < 0 ) return false if ( DotProduct( topVec, pointToTop ) < 0.0 ) return false return true } float function AngleDiff( float ang, float targetAng ) { float delta = ( targetAng - ang ) % 360.0 if ( targetAng > ang ) { if ( delta >= 180.0 ) delta -= 360.0; } else { if ( delta <= -180.0 ) delta += 360.0; } return delta } float function ClampAngle( float ang ) { while( ang > 360 ) ang -= 360 while( ang < 0 ) ang += 360 return ang } float function ClampAngle180( float ang ) { while( ang > 180 ) ang -= 180 while( ang < -180 ) ang += 180 return ang } vector function ShortestRotation( vector ang, vector targetAng ) { return Vector( AngleDiff( ang.x, targetAng.x ), AngleDiff( ang.y, targetAng.y ), AngleDiff( ang.z, targetAng.z ) ) } int function GetWinningTeam() { if ( level.nv.winningTeam != null ) return expect int( level.nv.winningTeam ) if ( IsFFAGame() ) return GetWinningTeam_FFA() if ( IsRoundBased() ) { if ( GameRules_GetTeamScore2( TEAM_IMC ) > GameRules_GetTeamScore2( TEAM_MILITIA ) ) return TEAM_IMC if ( GameRules_GetTeamScore2( TEAM_MILITIA ) > GameRules_GetTeamScore2( TEAM_IMC ) ) return TEAM_MILITIA } else { if ( GameRules_GetTeamScore( TEAM_IMC ) > GameRules_GetTeamScore( TEAM_MILITIA ) ) return TEAM_IMC if ( GameRules_GetTeamScore( TEAM_MILITIA ) > GameRules_GetTeamScore( TEAM_IMC ) ) return TEAM_MILITIA } return TEAM_UNASSIGNED } int function GetWinningTeam_FFA() { if ( level.nv.winningTeam != null ) return expect int( level.nv.winningTeam ) int maxScore = 0 int playerTeam int currentScore int winningTeam = TEAM_UNASSIGNED foreach( player in GetPlayerArray() ) { playerTeam = player.GetTeam() if ( IsRoundBased() ) currentScore = GameRules_GetTeamScore2( playerTeam ) else currentScore = GameRules_GetTeamScore( playerTeam ) if ( currentScore == maxScore) //Treat multiple teams as having the same score as no team winning winningTeam = TEAM_UNASSIGNED if ( currentScore > maxScore ) { maxScore = currentScore winningTeam = playerTeam } } return winningTeam } void function EmitSkyboxSoundAtPosition( vector positionInSkybox, string sound, float skyboxScale = 0.001, bool clamp = false ) { if ( IsServer() ) clamp = true // sounds cannot play outside 16k limit on server vector position = SkyboxToWorldPosition( positionInSkybox, skyboxScale, clamp ) EmitSoundAtPosition( TEAM_UNASSIGNED, position, sound ) } vector function SkyboxToWorldPosition( vector positionInSkybox, float skyboxScale = 0.001, bool clamp = true ) { Assert( skyboxScale > 0 ) Assert( "skyboxCamOrigin" in level ) vector position = Vector( 0.0, 0.0, 0.0 ) vector skyOrigin = expect vector( level.skyboxCamOrigin ) #if CLIENT position = ( positionInSkybox - skyOrigin ) * ( 1.0 / skyboxScale ) if ( clamp ) { entity localViewPlayer = GetLocalViewPlayer() Assert( localViewPlayer ) vector localViewPlayerOrg = localViewPlayer.GetOrigin() position = localViewPlayerOrg + ClampVectorToCube( localViewPlayerOrg, position - localViewPlayerOrg, Vector( 0.0, 0.0, 0.0 ), 32000.0 ) } #else position = ( positionInSkybox - skyOrigin ) * ( 1.0 / skyboxScale ) if ( clamp ) position = ClampVectorToCube( Vector( 0.0, 0.0, 0.0 ), position, Vector( 0.0, 0.0, 0.0 ), 32000.0 ) #endif // CLIENT return position } void function FadeOutSoundOnEntityAfterDelay( entity ent, string soundAlias, float delay, float fadeTime ) { if ( !IsValid( ent ) ) return ent.EndSignal( "OnDestroy" ) wait delay FadeOutSoundOnEntity( ent, soundAlias, fadeTime ) } function GetRandomKeyFromWeightedTable( Table ) { local weightTotal = 0.0 foreach ( key, value in Table ) { weightTotal += value } local randomValue = RandomFloat( weightTotal ) foreach ( key, value in Table ) { if ( randomValue <= weightTotal && randomValue >= weightTotal - value) return key weightTotal -= value } } bool function IsMatchOver() { if ( IsRoundBased() && level.nv.gameEndTime ) return true else if ( !IsRoundBased() && level.nv.gameEndTime && Time() > level.nv.gameEndTime ) return true return false } bool function IsScoringNonStandard() { return expect bool( level.nv.nonStandardScoring ) } bool function IsRoundBased() { return expect bool( level.nv.roundBased ) } int function GetRoundsPlayed() { return expect int( level.nv.roundsPlayed ) } bool function IsEliminationBased() { return Riff_EliminationMode() != eEliminationMode.Default } bool function IsPilotEliminationBased() { return ( Riff_EliminationMode() == eEliminationMode.Pilots || Riff_EliminationMode() == eEliminationMode.PilotsTitans ) } bool function IsTitanEliminationBased() { return ( Riff_EliminationMode() == eEliminationMode.Titans || Riff_EliminationMode() == eEliminationMode.PilotsTitans ) } bool function IsSingleTeamMode() { return ( 1 == GetCurrentPlaylistVarInt( "max_teams", 2 ) ) } void function __WarpInEffectShared( vector origin, vector angles, string sfx, float preWaitOverride = -1.0 ) { float preWait = 2.0 float sfxWait = 0.1 float totalTime = WARPINFXTIME if ( sfx == "" ) sfx = "dropship_warpin" if ( preWaitOverride >= 0.0 ) wait preWaitOverride else wait preWait //this needs to go and the const for warpin fx time needs to change - but not this game - the intro system is too dependent on it #if CLIENT int fxIndex = GetParticleSystemIndex( FX_GUNSHIP_CRASH_EXPLOSION_ENTRANCE ) StartParticleEffectInWorld( fxIndex, origin, angles ) #else entity fx = PlayFX( FX_GUNSHIP_CRASH_EXPLOSION_ENTRANCE, origin, angles ) fx.FXEnableRenderAlways() fx.DisableHibernation() #endif // CLIENT wait sfxWait EmitSoundAtPosition( TEAM_UNASSIGNED, origin, sfx ) wait totalTime - preWait - sfxWait } void function __WarpOutEffectShared( entity dropship ) { int attach = dropship.LookupAttachment( "origin" ) vector origin = dropship.GetAttachmentOrigin( attach ) vector angles = dropship.GetAttachmentAngles( attach ) #if CLIENT int fxIndex = GetParticleSystemIndex( FX_GUNSHIP_CRASH_EXPLOSION_EXIT ) StartParticleEffectInWorld( fxIndex, origin, angles ) #else entity fx = PlayFX( FX_GUNSHIP_CRASH_EXPLOSION_EXIT, origin, angles ) fx.FXEnableRenderAlways() fx.DisableHibernation() #endif // CLIENT EmitSoundAtPosition( TEAM_UNASSIGNED, origin, "dropship_warpout" ) } bool function IsSwitchSidesBased() { return (level.nv.switchedSides != null) } int function HasSwitchedSides() //This returns an int instead of a bool! Should rewrite { return expect int( level.nv.switchedSides ) } bool function IsFirstRoundAfterSwitchingSides() { if ( !IsSwitchSidesBased() ) return false if ( IsRoundBased() ) return level.nv.switchedSides > 0 && GetRoundsPlayed() == level.nv.switchedSides else return level.nv.switchedSides > 0 unreachable } void function CamBlendFov( entity cam, float oldFov, float newFov, float transTime, float transAccel, float transDecel ) { if ( !IsValid( cam ) ) return cam.EndSignal( "OnDestroy" ) float currentTime = Time() float startTime = currentTime float endTime = startTime + transTime while ( endTime > currentTime ) { float interp = Interpolate( startTime, endTime - startTime, transAccel, transDecel ) cam.SetFOV( GraphCapped( interp, 0.0, 1.0, oldFov, newFov ) ) wait( 0.0 ) currentTime = Time() } } void function CamFollowEnt( entity cam, entity ent, float duration, vector offset = <0.0, 0.0, 0.0>, string attachment = "", bool isInSkybox = false ) { if ( !IsValid( cam ) ) return cam.EndSignal( "OnDestroy" ) vector camOrg = Vector( 0.0, 0.0, 0.0 ) vector targetPos = Vector( 0.0, 0.0, 0.0 ) float currentTime = Time() float startTime = currentTime float endTime = startTime + duration vector diff = Vector( 0.0, 0.0, 0.0 ) int attachID = ent.LookupAttachment( attachment ) while ( endTime > currentTime ) { camOrg = cam.GetOrigin() if ( attachID <= 0 ) targetPos = ent.GetOrigin() else targetPos = ent.GetAttachmentOrigin( attachID ) if ( isInSkybox ) targetPos = SkyboxToWorldPosition( targetPos ) diff = ( targetPos + offset ) - camOrg cam.SetAngles( VectorToAngles( diff ) ) wait( 0.0 ) currentTime = Time() } } void function CamFacePos( entity cam, vector pos, float duration ) { if ( !IsValid( cam ) ) return cam.EndSignal( "OnDestroy" ) float currentTime = Time() float startTime = currentTime float endTime = startTime + duration vector diff = Vector( 0.0, 0.0, 0.0 ) while ( endTime > currentTime ) { diff = pos - cam.GetOrigin() cam.SetAngles( VectorToAngles( diff ) ) wait( 0.0 ) currentTime = Time() } } void function CamBlendFromFollowToAng( entity cam, entity ent, vector endAng, float transTime, float transAccel, float transDecel ) { if ( !IsValid( cam ) ) return cam.EndSignal( "OnDestroy" ) vector camOrg = cam.GetOrigin() float currentTime = Time() float startTime = currentTime float endTime = startTime + transTime while ( endTime > currentTime ) { vector diff = ent.GetOrigin() - camOrg vector anglesToEnt = VectorToAngles( diff ) float frac = Interpolate( startTime, endTime - startTime, transAccel, transDecel ) vector newAngs = anglesToEnt + ShortestRotation( anglesToEnt, endAng ) * frac cam.SetAngles( newAngs ) wait( 0.0 ) currentTime = Time() } } void function CamBlendFromPosToPos( entity cam, vector startPos, vector endPos, float transTime, float transAccel, float transDecel ) { if ( !IsValid( cam ) ) return cam.EndSignal( "OnDestroy" ) float currentTime = Time() float startTime = currentTime float endTime = startTime + transTime vector diff = endPos - startPos while ( endTime > currentTime ) { float frac = Interpolate( startTime, endTime - startTime, transAccel, transDecel ) vector newAngs = startPos + diff * frac cam.SetOrigin( newAngs ) wait( 0.0 ) currentTime = Time() } } void function CamBlendFromAngToAng( entity cam, vector startAng, vector endAng, float transTime, float transAccel, float transDecel ) { if ( !IsValid( cam ) ) return cam.EndSignal( "OnDestroy" ) float currentTime = Time() float startTime = currentTime float endTime = startTime + transTime while ( endTime > currentTime ) { float frac = Interpolate( startTime, endTime - startTime, transAccel, transDecel ) vector newAngs = startAng + ShortestRotation( startAng, endAng ) * frac cam.SetAngles( newAngs ) wait( 0.0 ) currentTime = Time() } } int function AddBitMask( int bitsExisting, int bitsToAdd ) { return bitsExisting | bitsToAdd } int function RemoveBitMask( int bitsExisting, int bitsToRemove ) { return bitsExisting & ( ~bitsToRemove ) } bool function HasBitMask( int bitsExisting, int bitsToCheck ) { int bitsCommon = bitsExisting & bitsToCheck return bitsCommon == bitsToCheck } float function GetDeathCamLength( entity player ) { if ( !GamePlayingOrSuddenDeath() ) return DEATHCAM_TIME_SHORT else return DEATHCAM_TIME unreachable } float function GetRespawnButtonCamTime( player ) { if ( !GamePlayingOrSuddenDeath() ) return DEATHCAM_TIME_SHORT + RESPAWN_BUTTON_BUFFER else return DEATHCAM_TIME + RESPAWN_BUTTON_BUFFER unreachable } float function GetKillReplayAfterTime( player ) { if ( IsSingleplayer() ) return 4.0 if ( !GamePlayingOrSuddenDeath() ) return KILL_REPLAY_AFTER_KILL_TIME_SHORT return KILL_REPLAY_AFTER_KILL_TIME } function IntroPreviewOn() { local bugnum = GetBugReproNum() switch( bugnum ) { case 1337: case 13371: case 13372: case 13373: case 1338: case 13381: case 13382: case 13383: return bugnum default: return null } } bool function EntHasModelSet( entity ent ) { asset modelName = ent.GetModelName() if ( modelName == $"" || modelName == $"?" ) return false return true } string function GenerateTitanOSAlias( entity player, string aliasSuffix ) { //HACK: Temp fix for blocker bug. Fixing correctly next. if ( IsSingleplayer() ) { return "diag_gs_titanBt_" + aliasSuffix } else { entity titan if ( player.IsTitan() ) titan = player else titan = player.GetPetTitan() Assert( IsValid( titan ) ) string titanCharacterName = GetTitanCharacterName( titan ) string primeTitanString = "" if ( IsTitanPrimeTitan( titan ) ) primeTitanString = "_prime" string modifiedAlias = "diag_gs_titan" + titanCharacterName + primeTitanString + "_" + aliasSuffix return modifiedAlias } unreachable } void function AddCallback_OnUseEntity( entity ent, callbackFunc ) { AssertParameters( callbackFunc, 2, "ent, player" ) if ( !( "onUseEntityCallbacks" in ent.s ) ) ent.s.onUseEntityCallbacks <- [] Assert( !ent.s.onUseEntityCallbacks.contains( callbackFunc ), "Already added " + FunctionToString( callbackFunc ) + " with AddCalback_OnUseEntity" ) ent.s.onUseEntityCallbacks.append( callbackFunc ) } void function SetWaveSpawnType( int spawnType ) { shGlobal.waveSpawnType = spawnType } int function GetWaveSpawnType() { return shGlobal.waveSpawnType } void function SetWaveSpawnInterval( float interval ) { shGlobal.waveSpawnInterval = interval } float function GetWaveSpawnInterval() { return shGlobal.waveSpawnInterval } bool function IsArcTitan( entity npc ) { return npc.GetAISettingsName() == "npc_titan_arc" } bool function IsNukeTitan( entity npc ) { return npc.GetAISettingsName() == "npc_titan_nuke" } bool function IsMortarTitan( entity npc ) { return npc.GetAISettingsName() == "npc_titan_mortar" } bool function IsFragDrone( entity npc ) { #if SERVER return npc.GetClassName() == "npc_frag_drone" #endif #if CLIENT return npc.GetSignifierName() == "npc_frag_drone" #endif } bool function IsSniperSpectre( entity npc ) { return false } bool function IsVortexSphere( entity ent ) { return ( ent.GetClassName() == "vortex_sphere" ) } bool function PointIsWithinBounds( vector point, vector mins, vector maxs ) { Assert( mins.x < maxs.x ) Assert( mins.y < maxs.y ) Assert( mins.z < maxs.z ) return ( ( point.z >= mins.z && point.z <= maxs.z ) && ( point.x >= mins.x && point.x <= maxs.x ) && ( point.y >= mins.y && point.y <= maxs.y ) ) } int function GetSpStartIndex() { //HACK -> this should use some other code driven thing, not GetBugReproNum int index = GetBugReproNum() if ( index < 0 ) return 0 return index } // return all living soldiers array function GetAllSoldiers() { return GetNPCArrayByClass( "npc_soldier" ) } int function GameTeams_GetNumLivingPlayers( int teamIndex = TEAM_ANY ) { int noOfLivingPlayers = 0 array players if ( teamIndex == TEAM_ANY ) players = GetPlayerArray() else players = GetPlayerArrayOfTeam( teamIndex ) foreach ( player in players ) { if ( !IsAlive( player ) ) continue ++noOfLivingPlayers } return noOfLivingPlayers } bool function GameTeams_TeamHasDeadPlayers( int team ) { array teamPlayers = GetPlayerArrayOfTeam( team ) foreach ( entity teamPlayer in teamPlayers ) { if ( !IsAlive( teamPlayer ) ) return true } return false } typedef EntitiesDidLoadCallbackType void functionref() array _EntitiesDidLoadTypedCallbacks void function RunCallbacks_EntitiesDidLoad() { // reloading the level so don't do callbacks if ( "forcedReloading" in level ) return foreach ( callback in _EntitiesDidLoadTypedCallbacks ) { thread callback() } } void function AddCallback_EntitiesDidLoad( EntitiesDidLoadCallbackType callback ) { _EntitiesDidLoadTypedCallbacks.append( callback ) } bool function IsTitanNPC( entity ent ) { return ent.IsTitan() && ent.IsNPC() } entity function InflictorOwner( entity inflictor ) { if ( IsValid( inflictor ) ) { entity inflictorOwner = inflictor.GetOwner() if ( IsValid( inflictorOwner ) ) inflictor = inflictorOwner } return inflictor } bool function IsPlayerControlledSpectre( entity ent ) { return ent.GetClassName() == "npc_spectre" && ent.GetBossPlayer() != null } bool function IsPlayerControlledTurret( entity ent ) { return IsTurret( ent ) && ent.GetBossPlayer() != null } bool function TitanShieldDecayEnabled() { return ( GetCurrentPlaylistVarInt( "titan_shield_decay", 0 ) == 1 ) } bool function TitanShieldRegenEnabled() { return ( GetCurrentPlaylistVarInt( "titan_shield_regen", 0 ) == 1 ) } bool function DoomStateDisabled() { return ( GetCurrentPlaylistVarString( "titan_doomstate_variation", "default" ) == "disabled" || GetCurrentPlaylistVarString( "titan_doomstate_variation", "default" ) == "lastsegment" ) } bool function NoWeaponDoomState() { return ( GetCurrentPlaylistVarString( "titan_doomstate_variation", "default" ) == "noweapon" ) } entity function GetPetTitanOwner( entity titan ) { array players = GetPlayerArray() entity foundPlayer foreach ( player in players ) { if ( player.GetPetTitan() == titan ) { Assert( foundPlayer == null, player + " and " + foundPlayer + " both own " + titan ) foundPlayer = player } } return foundPlayer } entity function GetSoulFromPlayer( entity player ) { Assert( player.IsPlayer(), "argument should be a player" ) if ( player.IsTitan() ) return player.GetTitanSoul() else if ( IsValid( player.GetPetTitan() ) ) return player.GetPetTitan().GetTitanSoul() return null } string function GetPlayerBodyType( player ) { return expect string( player.GetPlayerSettingsField( "weaponClass" ) ) } void function SetTeam( entity ent, int team ) { #if CLIENT ent.Code_SetTeam( team ) #else if ( ent.IsPlayer() ) { ent.Code_SetTeam( team ) } else if ( ent.IsNPC() ) { int currentTeam = ent.GetTeam() bool alreadyAssignedValidTeam = ( currentTeam == TEAM_IMC || currentTeam == TEAM_MILITIA ) ent.Code_SetTeam( team ) if ( ent.GetModelName() == $"" ) return FixupTitle( ent ) if ( IsGrunt( ent ) || IsSpectre( ent ) ) { if ( IsMultiplayer() ) { int eHandle = ent.GetEncodedEHandle() array players = GetPlayerArray() foreach ( player in players ) { Remote_CallFunction_Replay( player, "ServerCallback_UpdateOverheadIconForNPC", eHandle ) } } } else if ( IsShieldDrone( ent ) ) { if ( team == 0 ) { // anybody can use neutral shield drone ent.SetUsable() } else { // only friendlies use a team shield drone ent.SetUsableByGroup( "friendlies pilot" ) } } table modelTable = ent.CreateTableFromModelKeyValues() if ( !( "teamSkin" in modelTable ) ) return if ( alreadyAssignedValidTeam && ( !( "swapTeamOnLeech" in modelTable.teamSkin ) ) ) return SetSkinForTeam( ent, team ) } else { ent.Code_SetTeam( team ) } #endif } void function PrintTraceResults( TraceResults results ) { printt( "TraceResults: " ) printt( "=========================" ) printt( "hitEnt: " + results.hitEnt ) printt( "endPos: " + results.endPos ) printt( "surfaceNormal: " + results.surfaceNormal ) printt( "surfaceName: " + results.surfaceName ) printt( "fraction: " + results.fraction ) printt( "fractionLeftSolid: " + results.fractionLeftSolid ) printt( "hitGroup: " + results.hitGroup ) printt( "startSolid: " + results.startSolid ) printt( "allSolid: " + results.allSolid ) printt( "hitSky: " + results.hitSky ) printt( "contents: " + results.contents ) printt( "=========================" ) } bool function PROTO_AlternateDoomedState() { return ( GetCurrentPlaylistVarInt( "infinite_doomed_state", 1 ) == 1 ) } bool function PROTO_VariableRegenDelay() { return ( GetCurrentPlaylistVarInt( "variable_regen_delay", 1 ) == 1 ) } bool function PROTO_AutoTitansDisabled() { return ( GetCurrentPlaylistVarInt( "always_enable_autotitans", 1 ) == 0 ) } bool function TitanDamageRewardsTitanCoreTime() { if ( GetCurrentPlaylistVarInt( "titan_core_from_titan_damage", 0 ) != 0 ) return true return false } vector function ClampToMap( vector pos ) { return IterateAxis( pos, LimitAxisToMapExtents ) } vector function IterateAxis( vector pos, float functionref( float ) func ) { pos.x = func( pos.x ) pos.y = func( pos.y ) pos.z = func( pos.z ) return pos } float function LimitAxisToMapExtents( float axisVal ) { if ( axisVal >= MAP_EXTENTS ) axisVal = MAP_EXTENTS - 1 else if ( axisVal <= -MAP_EXTENTS ) axisVal = -( MAP_EXTENTS - 1 ) return axisVal } bool function PilotSpawnOntoTitanIsEnabledInPlaylist( entity player ) { if ( GetCurrentPlaylistVarInt( "titan_spawn_deploy_enabled", 0 ) != 0 ) return true return false } bool function PlayerCanSpawnIntoTitan( entity player ) { if ( !PilotSpawnOntoTitanIsEnabledInPlaylist( player ) ) return false entity titan = player.GetPetTitan() if ( !IsAlive( titan ) ) return false if ( GetDoomedState( titan ) ) return false if ( titan.ContextAction_IsActive() ) return false return false // turned off until todd figures out how to enable } array< vector > function EntitiesToOrigins( array< entity > ents ) { array origins foreach ( ent in ents ) { origins.append( ent.GetOrigin() ) } return origins } vector function GetMedianOriginOfEntities( array ents ) { array origins = EntitiesToOrigins( ents ) return GetMedianOrigin( origins ) } vector function GetMedianOrigin( array origins ) { if ( origins.len() == 1 ) return origins[0] vector median int middleIndex1 int middleIndex2 if ( IsEven( origins.len() ) ) { middleIndex1 = origins.len() / 2 middleIndex2 = middleIndex1 } else { middleIndex1 = int( floor( origins.len() / 2.0 ) ) middleIndex2 = middleIndex1 + 1 } origins.sort( CompareVecX ) median.x = ( origins[ middleIndex1 ].x + origins[ middleIndex2 ].x ) / 2.0 origins.sort( CompareVecY ) median.y = ( origins[ middleIndex1 ].y + origins[ middleIndex2 ].y ) / 2.0 origins.sort( CompareVecZ ) median.z = ( origins[ middleIndex1 ].z + origins[ middleIndex2 ].z ) / 2.0 return median } int function CompareVecX( vector a, vector b ) { if ( a.x > b.x ) return 1 return -1 } int function CompareVecY( vector a, vector b ) { if ( a.y > b.y ) return 1 return -1 } int function CompareVecZ( vector a, vector b ) { if ( a.z > b.z ) return 1 return -1 } float function GetFractionAlongPath( array nodes, vector p ) { float totalDistance = GetPathDistance( nodes ) // See which segment we are currently on (closest to) int closestSegment = -1 float closestDist = 9999 for( int i = 0 ; i < nodes.len() - 1; i++ ) { float dist = GetDistanceSqrFromLineSegment( nodes[i].GetOrigin(), nodes[i + 1].GetOrigin(), p ) if ( closestSegment < 0 || dist < closestDist ) { closestSegment = i closestDist = dist } } Assert( closestSegment >= 0 ) Assert( closestSegment < nodes.len() - 1 ) // Get the distance along the path already traveled float distTraveled = 0.0 for( int i = 0 ; i < closestSegment; i++ ) { //DebugDrawLine( nodes[i].GetOrigin(), nodes[i + 1].GetOrigin(), 255, 255, 0, true, 0.1 ) distTraveled += Distance( nodes[i].GetOrigin(), nodes[i+1].GetOrigin() ) } // Add the distance traveled on current segment vector closestPointOnSegment = GetClosestPointOnLineSegment( nodes[closestSegment].GetOrigin(), nodes[closestSegment + 1].GetOrigin(), p ) //DebugDrawLine( nodes[closestSegment].GetOrigin(), closestPointOnSegment, 255, 255, 0, true, 0.1 ) distTraveled += Distance( nodes[closestSegment].GetOrigin(), closestPointOnSegment ) return clamp( distTraveled / totalDistance, 0.0, 1.0 ) } float function GetPathDistance( array nodes ) { float totalDist = 0.0 for( int i = 0 ; i < nodes.len() - 1; i++ ) { //DebugDrawSphere( nodes[i].GetOrigin(), 16.0, 255, 0, 0, true, 0.1 ) totalDist += Distance( nodes[i].GetOrigin(), nodes[i+1].GetOrigin() ) } //DebugDrawSphere( nodes[nodes.len() -1].GetOrigin(), 16.0, 255, 0, 0, true, 0.1 ) return totalDist } void function WaittillAnimDone( entity animatingEnt ) { waitthread WaittillAnimDone_Thread( animatingEnt ) } void function WaittillAnimDone_Thread( entity animatingEnt ) { if ( animatingEnt.IsPlayer() ) animatingEnt.EndSignal( "OnDestroy" ) animatingEnt.EndSignal( "OnAnimationInterrupted" ) animatingEnt.WaitSignal( "OnAnimationDone" ) } array function GetEntityLinkChain( entity startNode ) { Assert( IsValid( startNode ) ) array nodes nodes.append( startNode ) while(true) { entity nextNode = nodes[nodes.len() - 1].GetLinkEnt() if ( !IsValid( nextNode ) ) break nodes.append( nextNode ) } return nodes } float function HealthRatio( entity ent ) { int health = ent.GetHealth() int maxHealth = ent.GetMaxHealth() return float( health ) / maxHealth } vector function GetPointOnPathForFraction( array nodes, float frac ) { Assert( frac >= 0 ) float totalPathDist = GetPathDistance( nodes ) float distRemaining = totalPathDist * frac vector point = nodes[0].GetOrigin() for( int i = 0 ; i < nodes.len() - 1; i++ ) { float segmentDist = Distance( nodes[i].GetOrigin(), nodes[i+1].GetOrigin() ) if ( segmentDist <= distRemaining ) { // Add the whole segment distRemaining -= segmentDist point = nodes[i+1].GetOrigin() } else { // Fraction ends somewhere in this segment vector dirVec = Normalize( nodes[i+1].GetOrigin() - nodes[i].GetOrigin() ) point = nodes[i].GetOrigin() + ( dirVec * distRemaining ) distRemaining = 0 } if ( distRemaining <= 0 ) break } if ( frac > 1.0 && distRemaining > 0 ) { vector dirVec = Normalize( nodes[nodes.len() - 1].GetOrigin() - nodes[nodes.len() - 2].GetOrigin() ) point = nodes[nodes.len() - 1].GetOrigin() + ( dirVec * distRemaining ) } return point } bool function PlayerBlockedByTeamEMP( entity player ) { return ( player.nv.empEndTime > Time() ) } #if SERVER void function Embark_Allow( entity player ) { player.SetTitanEmbarkEnabled( true ) } void function Embark_Disallow( entity player ) { player.SetTitanEmbarkEnabled( false ) } void function Disembark_Allow( entity player ) { player.SetTitanDisembarkEnabled( true ) } void function Disembark_Disallow( entity player ) { player.SetTitanDisembarkEnabled( false ) } #endif bool function CanEmbark( entity player ) { return player.GetTitanEmbarkEnabled() } bool function CanDisembark( entity player ) { return player.GetTitanDisembarkEnabled() } string function GetDroneType( entity npc ) { return expect string( npc.Dev_GetAISettingByKeyField( "drone_type" ) ) } vector function FlattenVector( vector vec ) { return Vector( vec.x, vec.y, 0 ) } vector function FlattenAngles( vector angles ) { return Vector( 0, angles.y, 0 ) } bool function IsHumanSized( entity ent ) { if ( ent.IsPlayer() ) return ent.IsHuman() if ( ent.IsNPC() ) { if ( ent.GetAIClass() == AIC_SMALL_TURRET ) return true string bodyType = ent.GetBodyType() return bodyType == "human" || bodyType == "marvin" } return false } bool function IsDropship( entity ent ) { #if SERVER return ent.GetClassName() == "npc_dropship" #elseif CLIENT if ( !ent.IsNPC() ) return false //Probably should not use GetClassName, but npc_dropship isn't a class so can't use instanceof? return ( ent.GetClassName() == "npc_dropship" || ent.GetSignifierName() == "npc_dropship" ) #endif } bool function IsSpecialist( entity ent ) { return IsGrunt( ent ) && ent.IsMechanical() } bool function IsGrunt( entity ent ) { #if SERVER return ent.IsNPC() && ent.GetClassName() == "npc_soldier" #elseif CLIENT return ent.IsNPC() && ent.GetSignifierName() == "npc_soldier" #endif } bool function IsMarvin( entity ent ) { return ent.IsNPC() && ent.GetAIClass() == AIC_MARVIN } bool function IsSpectre( entity ent ) { return ent.IsNPC() && ent.GetAIClass() == AIC_SPECTRE } bool function IsWorldSpawn( entity ent ) { #if SERVER return ent.GetClassName() == "worldspawn" #elseif CLIENT return ent.GetSignifierName() == "worldspawn" #endif } bool function IsEnvironment( entity ent ) { #if SERVER return ent.GetClassName() == "trigger_hurt" #elseif CLIENT return ent.GetSignifierName() == "trigger_hurt" #endif } bool function IsSuperSpectre( entity ent ) { #if SERVER return ent.GetClassName() == "npc_super_spectre" #elseif CLIENT return ent.GetSignifierName() == "npc_super_spectre" #endif } bool function IsAndroidNPC( entity ent ) { return ( IsSpectre( ent ) || IsStalker( ent ) || IsMarvin( ent ) ) } bool function IsStalker( entity ent ) { return ent.IsNPC() && ( ent.GetAIClass() == AIC_STALKER || ent.GetAIClass() == AIC_STALKER_CRAWLING ) } bool function IsProwler( entity ent ) { #if SERVER return ent.GetClassName() == "npc_prowler" #elseif CLIENT return ent.GetSignifierName() == "npc_prowler" #endif } bool function IsAirDrone( entity ent ) { #if SERVER return ent.GetClassName() == "npc_drone" #elseif CLIENT return ent.GetSignifierName() == "npc_drone" #endif } bool function IsPilotElite( entity ent ) { #if SERVER return ent.GetClassName() == "npc_pilot_elite" #elseif CLIENT return ent.GetSignifierName() == "npc_pilot_elite" #endif } bool function IsAttackDrone( entity ent ) { return ( ent.IsNPC() && !ent.IsNonCombatAI() && IsAirDrone( ent ) ) } bool function IsGunship( entity ent ) { #if SERVER return ent.GetClassName() == "npc_gunship" #elseif CLIENT return ent.GetSignifierName() == "npc_gunship" #endif } bool function IsMinion( entity ent ) { if ( IsGrunt( ent ) ) return true if ( IsSpectre( ent ) ) return true return false } bool function IsShieldDrone( entity ent ) { #if SERVER if ( ent.GetClassName() != "npc_drone" ) return false #elseif CLIENT if ( ent.GetSignifierName() != "npc_drone" ) return false #endif return GetDroneType( ent ) == "drone_type_shield" } #if SERVER bool function IsTick( entity ent ) { return (ent.IsNPC() && (ent.GetAIClass() == AIC_FRAG_DRONE)) } bool function IsNPCTitan( entity ent ) { return ent.IsNPC() && ent.IsTitan() } #endif bool function NPC_GruntChatterSPEnabled( entity npc ) { if ( !IsSingleplayer() ) return false if ( !npc.IsNPC() ) return false if ( npc.GetClassName() != "npc_soldier" ) return false return true } RaySphereIntersectStruct function IntersectRayWithSphere( vector rayStart, vector rayEnd, vector sphereOrigin, float sphereRadius ) { RaySphereIntersectStruct intersection vector vecSphereToRay = rayStart - sphereOrigin vector vecRayDelta = rayEnd - rayStart float a = DotProduct( vecRayDelta, vecRayDelta ) if ( a == 0.0 ) { intersection.result = LengthSqr( vecSphereToRay ) <= sphereRadius * sphereRadius intersection.enterFrac = 0.0 intersection.leaveFrac = 0.0 return intersection } float b = 2 * DotProduct( vecSphereToRay, vecRayDelta ) float c = DotProduct( vecSphereToRay, vecSphereToRay ) - sphereRadius * sphereRadius float discrim = b * b - 4 * a * c if ( discrim < 0.0 ) { intersection.result = false return intersection } discrim = sqrt( discrim ) float oo2a = 0.5 / a intersection.enterFrac = ( - b - discrim ) * oo2a intersection.leaveFrac = ( - b + discrim ) * oo2a if ( ( intersection.enterFrac > 1.0 ) || ( intersection.leaveFrac < 0.0 ) ) { intersection.result = false return intersection } if ( intersection.enterFrac < 0.0 ) intersection.enterFrac = 0.0 if ( intersection.leaveFrac > 1.0 ) intersection.leaveFrac = 1.0 intersection.result = true return intersection } table function GetTableFromString( string inString ) { if ( inString.len() > 0 ) return expect table( getconsttable()[ inString ] ) return {} } int function GetWeaponDamageNear( entity weapon, entity victim ) { entity weaponOwner = weapon.GetWeaponOwner() if ( weaponOwner.IsNPC() ) { if ( victim.GetArmorType() == ARMOR_TYPE_HEAVY ) return weapon.GetWeaponSettingInt( eWeaponVar.npc_damage_near_value_titanarmor ) else return weapon.GetWeaponSettingInt( eWeaponVar.npc_damage_near_value ) } else { if ( victim.GetArmorType() == ARMOR_TYPE_HEAVY ) return weapon.GetWeaponSettingInt( eWeaponVar.damage_near_value_titanarmor ) else return weapon.GetWeaponSettingInt( eWeaponVar.damage_near_value ) } unreachable } void function PrintFirstPersonSequenceStruct( FirstPersonSequenceStruct fpsStruct ) { printt( "Printing FirstPersonSequenceStruct:" ) printt( "firstPersonAnim: " + fpsStruct.firstPersonAnim ) printt( "thirdPersonAnim: " + fpsStruct.thirdPersonAnim ) printt( "firstPersonAnimIdle: " + fpsStruct.firstPersonAnimIdle ) printt( "thirdPersonAnimIdle: " + fpsStruct.thirdPersonAnimIdle ) printt( "relativeAnim: " + fpsStruct.relativeAnim ) printt( "attachment: " + fpsStruct.attachment ) printt( "teleport: " + fpsStruct.teleport ) printt( "noParent: " + fpsStruct.noParent ) printt( "blendTime: " + fpsStruct.blendTime ) printt( "noViewLerp: " + fpsStruct.noViewLerp ) printt( "hideProxy: " + fpsStruct.hideProxy ) printt( "viewConeFunction: " + string( fpsStruct.viewConeFunction ) ) printt( "origin: " + string( fpsStruct.origin ) ) printt( "angles: " + string ( fpsStruct.angles ) ) printt( "enablePlanting: " + fpsStruct.enablePlanting ) printt( "setInitialTime: " + fpsStruct.setInitialTime ) printt( "useAnimatedRefAttachment: " + fpsStruct.useAnimatedRefAttachment ) printt( "renderWithViewModels: " + fpsStruct.renderWithViewModels ) printt( "gravity: " + fpsStruct.gravity ) } void function WaitSignalOrTimeout( entity ent, float timeout, string signal1, string signal2 = "", string signal3 = "" ) { Assert( IsValid( ent ) ) ent.EndSignal( signal1 ) if ( signal2 != "" ) ent.EndSignal( signal2 ) if ( signal3 != "" ) ent.EndSignal( signal3 ) wait( timeout ) } array function GetShortestLineSegmentConnectingLineSegments( vector line1Point1, vector line1Point2, vector line2Point1, vector line2Point2 ) { // From Paul Bourke's algorithm "The shortest line between two lines in 3D" at http://paulbourke.net/geometry/pointlineplane/ vector p1 = line1Point1 vector p2 = line1Point2 vector p3 = line2Point1 vector p4 = line2Point2 vector p13 = p1 - p3 vector p21 = p2 - p1 vector p43 = p4 - p3 if ( Length( p43 ) < 1.0 ) { array resultVectors resultVectors.append( p4 ) resultVectors.append( p3 ) return resultVectors } if ( Length( p21 ) < 1.0 ) { array resultVectors resultVectors.append( p2 ) resultVectors.append( p1 ) return resultVectors } float d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z float d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z float d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z float d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z float d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z float denom = d2121 * d4343 - d4321 * d4321 Assert( fabs( denom ) > 0.01 ) float numer = d1343 * d4321 - d1321 * d4343 float mua = numer / denom float mub = (d1343 + d4321 * (mua)) / d4343 vector resultVec1 vector resultVec2 resultVec1.x = p1.x + mua * p21.x resultVec1.y = p1.y + mua * p21.y resultVec1.z = p1.z + mua * p21.z resultVec2.x = p3.x + mub * p43.x resultVec2.y = p3.y + mub * p43.y resultVec2.z = p3.z + mub * p43.z array resultVectors resultVectors.append( resultVec1 ) resultVectors.append( resultVec2 ) return resultVectors } vector function GetClosestPointToLineSegments( vector line1Point1, vector line1Point2, vector line2Point1, vector line2Point2 ) { array results = GetShortestLineSegmentConnectingLineSegments( line1Point1, line1Point2, line2Point1, line2Point2 ) Assert( results.len() == 2 ) return ( results[0] + results[1] ) / 2.0 } bool function PlayerCanSee( entity player, entity ent, bool doTrace, float degrees ) { float minDot = deg_cos( degrees ) // On screen? float dot = DotProduct( Normalize( ent.GetWorldSpaceCenter() - player.EyePosition() ), player.GetViewVector() ) if ( dot < minDot ) return false // Can trace to it? if ( doTrace ) { TraceResults trace = TraceLine( player.EyePosition(), ent.GetWorldSpaceCenter(), null, TRACE_MASK_BLOCKLOS, TRACE_COLLISION_GROUP_NONE ) if ( trace.hitEnt == ent || trace.fraction >= 0.99 ) return true else return false } else return true Assert( 0, "shouldn't ever get here") unreachable } bool function PlayerCanSeePos( entity player, vector pos, bool doTrace, float degrees ) { float minDot = deg_cos( degrees ) float dot = DotProduct( Normalize( pos - player.EyePosition() ), player.GetViewVector() ) if ( dot < minDot ) return false if ( doTrace ) { TraceResults trace = TraceLine( player.EyePosition(), pos, null, TRACE_MASK_BLOCKLOS, TRACE_COLLISION_GROUP_NONE ) if ( trace.fraction < 0.99 ) return false } return true } bool function VectorsFacingSameDirection( vector v1, vector v2, float degreesThreshold ) { float minDot = deg_cos( degreesThreshold ) float dot = DotProduct( Normalize( v1 ), Normalize( v2 ) ) return ( dot >= minDot ) } vector function GetRelativeDelta( vector origin, entity ref, string attachment = "" ) { vector pos vector right vector forward vector up if ( attachment != "" ) { int attachID = ref.LookupAttachment( attachment ) pos = ref.GetAttachmentOrigin( attachID ) vector angles = ref.GetAttachmentAngles( attachID ) right = AnglesToRight( angles ) forward = AnglesToForward( angles ) up = AnglesToUp( angles ) } else { pos = ref.GetOrigin() right = ref.GetRightVector() forward = ref.GetForwardVector() up = ref.GetUpVector() } vector x = GetClosestPointOnLineSegment( pos + right * -16384, pos + right * 16384, origin ) vector y = GetClosestPointOnLineSegment( pos + forward * -16384, pos + forward * 16384, origin ) vector z = GetClosestPointOnLineSegment( pos + up * -16384, pos + up * 16384, origin ) float distx = Distance(pos, x) float disty = Distance(pos, y) float distz = Distance(pos, z) if ( DotProduct( x - pos, right ) < 0 ) distx *= -1 if ( DotProduct( y - pos, forward ) < 0 ) disty *= -1 if ( DotProduct( z - pos, up ) < 0 ) distz *= -1 return Vector( distx, disty, distz ) } #if SERVER float function GetRoundTimeLimit_ForGameMode() { #if DEV if ( level.devForcedTimeLimit ) { //Make it needed to be called multiple times for RoundBasedGameModes level.devForcedTimeLimit = 0 return 0.1 } #endif #if MP if ( GameState_GetTimeLimitOverride() >= 0 ) return GameState_GetTimeLimitOverride() #endif if ( !GameMode_IsDefined( GAMETYPE ) ) return GetCurrentPlaylistVarFloat( "roundtimelimit", 10 ) else return GameMode_GetRoundTimeLimit( GAMETYPE ) unreachable } #endif bool function HasIronRules() { bool result = (GetCurrentPlaylistVarInt( "iron_rules", 0 ) != 0) return result } vector function GetWorldOriginFromRelativeDelta( vector delta, entity ref ) { vector right = ref.GetRightVector() * delta.x vector forward = ref.GetForwardVector() * delta.y vector up = ref.GetUpVector() * delta.z return ref.GetOrigin() + right + forward + up } bool function IsHardcoreGameMode() { return GetCurrentPlaylistVarInt( "gm_hardcore_settings", 0 ) == 1 } bool function PlayerHasWeapon( entity player, string weaponName ) { array weapons = player.GetMainWeapons() weapons.extend( player.GetOffhandWeapons() ) foreach ( weapon in weapons ) { if ( weapon.GetWeaponClassName() == weaponName ) return true } return false } bool function PlayerCanUseWeapon( entity player, string weaponClass ) { return ( ( player.IsTitan() && weaponClass == "titan" ) || ( !player.IsTitan() && weaponClass == "human" ) ) } string function GetTitanCharacterName( entity titan ) { Assert( titan.IsTitan() ) string setFile if ( titan.IsPlayer() ) { setFile = titan.GetPlayerSettings() } else { string aiSettingsFile = titan.GetAISettingsName() setFile = expect string( Dev_GetAISettingByKeyField_Global( aiSettingsFile, "npc_titan_player_settings" ) ) } return GetTitanCharacterNameFromSetFile( setFile ) } bool function IsTitanPrimeTitan( entity titan ) { Assert( titan.IsTitan() ) string setFile if ( titan.IsPlayer() ) { setFile = titan.GetPlayerSettings() } else { string aiSettingsFile = titan.GetAISettingsName() setFile = expect string( Dev_GetAISettingByKeyField_Global( aiSettingsFile, "npc_titan_player_settings" ) ) } return Dev_GetPlayerSettingByKeyField_Global( setFile, "isPrime" ) == 1 }