From 60d3c12955ff03ba594f16c863258994a995c39d Mon Sep 17 00:00:00 2001 From: Connie Price Date: Thu, 20 Jan 2022 19:43:23 +0000 Subject: Moved Join to a global JoinStringArray so that anyone can use it. - Added `sh_utility_all.gnut` so that we can add global utility functions to all three squirrel VMs ( UI, Client & Server ) - Also removed a rogue leftover constant. --- .../mod/scripts/vscripts/sh_utility_all.gnut | 1585 ++++++++++++++++++++ 1 file changed, 1585 insertions(+) create mode 100644 Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut (limited to 'Northstar.CustomServers') diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut b/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut new file mode 100644 index 000000000..9eb673a15 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_utility_all.gnut @@ -0,0 +1,1585 @@ +untyped + +globalize_all_functions + +global const DEG_TO_RAD = 0.01745329251994 // PI / 180.0 +global const RAD_TO_DEG = 57.29577951308232 // 180.0 / PI + +global array LEGAL_PLAYER_TITAN_SETTINGS + +global struct AllModesAndMapsCompleteData +{ + int required + int progress +} + +struct +{ + int lastHostThreadMode + int lastScriptPrecacheErrors + int lastReportFatal + bool devUnlockedSPMissions +} file + +void function ShUtilityAll_Init() +{ + #document( "DistanceAlongVector", "" ) + #document( "GetClosestPointOnLineSegment", "Get the nearest point on a line segment" ) + #document( "GetDistanceFromLineSegment", "" ) + #document( "GetDistanceSqrFromLineSegment", "" ) + + LEGAL_PLAYER_TITAN_SETTINGS = GetAllowedPlayerTitanSettings() //Function reads from the data table, no point doing it over and over when it shouldn't change mid-match/game +} + +bool function IsMultiplayer() +{ + #if UI + return GetConVarString( "mp_gamemode" ) != "solo" + #else + return GAMETYPE != GAMEMODE_SP + #endif +} + +bool function IsSingleplayer() +{ + #if UI + return GetConVarString( "mp_gamemode" ) == "solo" + #else + return GAMETYPE == GAMEMODE_SP + #endif +} + +function PrintObject( obj, indent, depth, maxDepth ) +{ + if ( IsTable( obj ) ) + { + if ( depth >= maxDepth ) + { + printl( "{...}" ) + return + } + + printl( "{" ) + foreach ( k, v in obj ) + { + print( TableIndent( indent + 2 ) + k + " = " ) + PrintObject( v, indent + 2, depth + 1, maxDepth ) + } + printl( TableIndent( indent ) + "}" ) + } + else if ( IsArray( obj ) ) + { + if ( depth >= maxDepth ) + { + printl( "[...]" ) + return + } + + printl( "[" ) + foreach ( v in obj ) + { + print( TableIndent( indent + 2 ) ) + PrintObject( v, indent + 2, depth + 1, maxDepth ) + } + printl( TableIndent( indent ) + "]" ) + } + else if ( obj != null ) + { + printl( "" + obj ) + } + else + { + printl( "" ) + } +} + +string function FunctionToString( func ) +{ + Assert( func, "No function passed" ) + Assert( type( func ) == "function", "Type " + type( func ) + " is not a function" ) + + return expect string( func.getinfos().name ) +} + +// dump the stack trace to the console +function DumpStack( int offset = 1 ) +{ + for ( int i = offset; i < 20; i++ ) + { + if ( !( "src" in getstackinfos(i) ) ) + break + printl( i + " File : " + getstackinfos(i)["src"] + " [" + getstackinfos(i)["line"] + "]\n Function : " + getstackinfos(i)["func"] + "() " ) + } +} + +function DumpPreviousFunction() +{ + int i = 3 + if ( !( "src" in getstackinfos(i) ) ) + return + printl( "Called from: " + getstackinfos(i)["src"] + " [" + getstackinfos(i)["line"] + "] : " + getstackinfos(i)["func"] + "() " ) +} + +function GetPreviousFunction() +{ + int i = 3 + if ( !( "src" in getstackinfos(i) ) ) + return "" + return "Called from: " + getstackinfos(i)["src"] + " [" + getstackinfos(i)["line"] + "] : " + getstackinfos(i)["func"] + "() " +} + +bool function IsNewThread() +{ + //return threads.GetCurrentThread().co == getthread() + int i + for ( i = 0; i < 20; i++ ) + { + if ( !( "src" in getstackinfos(i) ) ) + break + } + + return i == 3 +} + +function AssertParameters( func, paramCount, paramDesc ) +{ + local funcInfos = func.getinfos() + local funcName = funcInfos.name + // subtract one from the param count for the hidden "this" object + Assert( funcInfos.parameters.len() == (paramCount + 1), "Function \"" + funcName +"\" must have exactly " + paramCount + " parameters (" + paramDesc + ")." ) +} + +function PrintTable( tbl, indent = 0, maxDepth = 4 ) +{ + print( TableIndent( indent ) ) + PrintObject( tbl, indent, 0, maxDepth ) +} + +string function TableIndent( indent ) +{ + return (" ").slice( 0, indent ) +} + +function GetHattID( id ) +{ + local result = getconsttable()[ id ] + //printt( "This is hattID: " + result ) + return getconsttable()[ id ] +} + +function GetCurrentPlaylistVar( val, opVal = null ) +{ + #if UI + return Code_GetCurrentPlaylistVar( val ) + #else + if ( opVal == null ) + return Code_GetCurrentPlaylistVar( val ) + + return GetCurrentPlaylistVarOrUseValue( val, opVal ) + #endif +} + +function GetCurrentPlaylistVarOrUseValue( val, opVal ) +{ + if ( typeof opVal != "string" ) + opVal = string( opVal ) + + #if !SERVER + if ( !IsConnected() ) + return opVal + #endif + + return Code_GetCurrentPlaylistVarOrUseValue( val, opVal ) // HACK +} + +int function GetCurrentPlaylistVarInt( string val, int useVal ) +{ + local result = GetCurrentPlaylistVarOrUseValue( val, useVal ) + if ( result == null ) + return 0 + + return int( result ) +} + +// Return a random entry from a table +function RandomTable( Table ) +{ + Assert( type( Table ) == "table", "Not a table" ) + if ( Table.len() == 0 ) + return null + + int index = RandomInt( Table.len() ) + int i = 0 + foreach ( entry in Table ) + { + if ( i != index ) + { + i++ + continue + } + return entry + } +} + +bool function IsMultiplayerPlaylist() +{ + return GetCurrentPlaylistVarInt( "classic_mp", 0 ) ? true : false +} + +function SortLowest( a, b ) +{ + if ( a > b ) + return 1 + + if ( a < b ) + return -1 + + return 0 +} + +function SortHighest( a, b ) +{ + if ( a < b ) + return 1 + + if ( a > b ) + return -1 + + return 0 +} + +float function DegToRad( float degrees ) +{ + return degrees * DEG_TO_RAD +} + +float function RadToDeg( float radians ) +{ + return radians * RAD_TO_DEG +} + +vector function RotateAroundOrigin2D( vector originToRotate, vector origin, float angRadians ) +{ + vector rotated = Vector( 0.0, 0.0, originToRotate.z ) + float sinOffsetAng = sin( angRadians ) + float cosOffsetAng = cos( angRadians ) + vector offset = originToRotate - origin + + rotated.x = origin.x + ( offset.x * cosOffsetAng ) - ( offset.y * sinOffsetAng ) + rotated.y = origin.y + ( offset.x * sinOffsetAng ) + ( offset.y * cosOffsetAng ) + + return rotated +} + +bool function IsLobbyMapName( string levelname ) +{ + if ( levelname == "mp_lobby" ) + return true + + return false +} + +bool function IsLobby() +{ + string mapName + + #if UI + mapName = GetActiveLevel() + #else + mapName = GetMapName() + #endif + + return IsLobbyMapName( mapName ) +} + +bool function IsFFAGame() +{ + return ( MAX_TEAMS > 2 ) +} + +int function GetEnemyTeam( int team ) +{ + if ( IsFFAGame() ) + return TEAM_UNASSIGNED + + Assert( team == TEAM_IMC || team == TEAM_MILITIA ) + + return (TEAM_IMC + TEAM_MILITIA) - team +} + +string function GetTeamName( int team ) +{ + #if UI + Assert( team == TEAM_IMC || team == TEAM_MILITIA ) + #endif + + if ( team == TEAM_IMC ) + return "#FACTION_APEX" + + if ( team == TEAM_MILITIA ) + return "#FACTION_64" + + return "" +} + +string function GetTeamShortName( int team ) +{ + if ( team == TEAM_IMC ) + return "APEX" + + if ( team == TEAM_MILITIA ) + return "6-4" + + return "" +} + +string function GetMapDisplayNameAllCaps( string mapname ) +{ + return "#" + mapname + "_ALLCAPS" +} + +string function GetMapDisplayName( string mapname ) +{ + return "#" + mapname +} + +string function GetMapDisplayDesc( string mapname ) +{ + return "#" + mapname + "_CLASSIC_DESC" +} + +string function StringReplace( string baseString, string searchString, string replaceString ) +{ + var findResult = baseString.find( searchString ) + + if ( findResult != null ) + { + string part1 = baseString.slice( 0, findResult ) + string part2 = baseString.slice( findResult + searchString.len(), baseString.len() ) + + baseString = part1 + replaceString + part2 + } + + return baseString +} + +// Returns float +function RoundToNearestInt( value ) +{ + return floor( value + 0.5 ) +} + +float function RoundToNearestMultiplier( float value, float multiplier ) +{ + float remainder = value % multiplier + + value -= remainder + + if ( remainder >= ( multiplier / 2 ) ) + value += multiplier + + return value +} + +function DevEverythingUnlocked() +{ + return EverythingUnlockedConVarEnabled() +} + + +table function GetByTitanTypes() +{ + return { + byTitan_ion = "ion" + byTitan_scorch = "scorch" + byTitan_northstar = "northstar" + byTitan_ronin = "ronin" + byTitan_tone = "tone" + byTitan_legion = "legion" + byTitan_vanguard = "vanguard" + } +} + +table function GetAsTitanTypes() +{ + return { + asTitan_ion = "ion" + asTitan_scorch = "scorch" + asTitan_northstar = "northstar" + asTitan_ronin = "ronin" + asTitan_tone = "tone" + asTitan_legion = "legion" + asTitan_vanguard = "vanguard" + } +} + +table function GetAsNPCTitanTypes() +{ + return { + byNPCTitans_ion = "ion" + byNPCTitans_scorch = "scorch" + byNPCTitans_northstar = "northstar" + byNPCTitans_ronin = "ronin" + byNPCTitans_tone = "tone" + byNPCTitans_legion = "legion" + byNPCTitans_vanguard = "vanguard" + } +} + +table function GetPluralTitanTypes() +{ + return { + titans_ion = "ion" + titans_scorch = "scorch" + titans_northstar = "northstar" + titans_ronin = "ronin" + titans_tone = "tone" + titans_legion = "legion" + titans_vanguard = "vanguard" + } +} + +table function GetCapitalizedTitanTypes() +{ + return { + Ion = "ion" + Scorch = "scorch" + Northstar = "northstar" + Ronin = "ronin" + Tone = "tone" + Legion = "legion" + Vanguard = "vanguard" + } +} + +bool function ValidateTitanType( titanSubClass ) +{ + switch ( titanSubClass ) + { + case "ion": + case "scorch": + case "northstar": + case "ronin": + case "tone": + case "legion": + case "vanguard": + return true + } + + return false +} + +function GetWeaponInfoFileKeyField_GlobalNotNull( ref, variable ) +{ + local val = GetWeaponInfoFileKeyField_Global( ref, variable ) + Assert( val != null, "Tried to get ref " + ref + " var " + variable + " but it was null" ) + return val +} + +bool function IsWeaponKeyFieldDefined( string ref, string variable ) +{ + var val = GetWeaponInfoFileKeyField_Global( ref, variable ) + + if ( val != null ) + return true + + return false +} + +string function GetWeaponInfoFileKeyField_GlobalString( string ref, string variable ) +{ + var val = GetWeaponInfoFileKeyField_Global( ref, variable ) + Assert( val != null, "Weapon key \"" + variable + "\" does not exist in weapon \"" + ref + "\" !" ) + return expect string( val ) +} + +int function GetWeaponInfoFileKeyField_GlobalInt( string ref, string variable ) +{ + var val = GetWeaponInfoFileKeyField_Global( ref, variable ) + Assert( val != null, "Weapon key \"" + variable + "\" does not exist in weapon \"" + ref + "\" !" ) + return expect int( val ) +} + +float function GetWeaponInfoFileKeyField_GlobalFloat( string ref, string variable ) +{ + var val = GetWeaponInfoFileKeyField_Global( ref, variable ) + Assert( val != null, "Weapon key \"" + variable + "\" does not exist in weapon \"" + ref + "\" !" ) + return expect float( val ) +} + +function PrintTimeParts( Table ) +{ + printt( " ", Table["month"] + "/" + Table["day"] + "/" + Table["year"], "-", Table["hour"] + ":" + Table["minute"] + ":" + Table["second"] ) +} + +function WaitFrame() +{ + //Don't use wait 0 since it doesn't actually wait a game frame. For example, if you have a client loop that does wait 0 even if the game is paused the loop will still run + wait 0.0001 +} + +function TableRemoveInvalidKeys( table Table ) +{ + array deleteKey = [] + + foreach ( key, value in Table ) + { + if ( !IsValid( key ) ) + deleteKey.append( key ) + } + + foreach ( key in deleteKey ) + { + // in this search, two things could end up on the same key + if ( key in Table ) + delete Table[ key ] + } +} + +string function VectorToString( vector vec ) +{ + return "Vector( " + vec.x + ", " + vec.y + ", " + vec.z + " )" +} + +bool function PROTO_RTS_HealthDisplay() +{ + return SharedPlaylistVarInt( "rts_style_health_bars", 0 ) > 0 +} + +int function SharedPlaylistVarInt( string name, int defaultVal ) +{ + #if UI + if ( !IsConnected() ) + return defaultVal + #endif + + return GetCurrentPlaylistVarInt( name, defaultVal ) +} + +function DeepClone( data ) +{ + if ( type( data ) == "table" ) + { + table newTable = {} + foreach( key, value in data ) + { + newTable[ key ] <- DeepClone( value ) + } + return newTable + } + else if ( type( data ) == "array" ) + { + array newArray = [] + for ( int i = 0; i < data.len(); i++ ) + { + newArray.append( DeepClone( data[ i ] ) ) + } + return newArray + } + else + { + return data + } +} + +vector function GetClosestPointOnPlane( vector a, vector b, vector c, vector p, bool clampInside = false ) +{ + vector n = CrossProduct( b - a, c - a ) + float eqTop = DotProduct( p - a, n ) + float eqBot = DotProduct( n, n ) + + //can't devide by 0 -> this is a degenerate triangle + if ( fabs( eqBot ) < 0.001 ) + return GetClosestPointOnLineSegment( a, b, p ) + + float magnitude = eqTop / eqBot + + vector endPoint = p - ( n * magnitude ) + + if ( clampInside ) + { + float testAB = DotProduct( CrossProduct( b - a, n ), p - a ) + float testBC = DotProduct( CrossProduct( c - b, n ), p - b ) + float testCA = DotProduct( CrossProduct( a - c, n ), p - c ) + + //if the results are negative - we're outside the triangle + if ( testAB * testBC < 0 || testBC * testCA < 0 ) + { + vector lineAB = GetClosestPointOnLineSegment( a, b, p ) + vector lineBC = GetClosestPointOnLineSegment( b, c, p ) + vector lineCA = GetClosestPointOnLineSegment( c, a, p ) + + vector closestVector = lineAB + float closestDist = DistanceSqr( p, lineAB ) + float dist = DistanceSqr( p, lineBC ) + if ( dist < closestDist ) + { + closestDist = dist + closestVector = lineBC + } + dist = DistanceSqr( p, lineCA ) + if ( dist < closestDist ) + { + closestDist = dist + closestVector = lineCA + } + return closestVector + } + //if we got this far - there are no outliers + } + + return endPoint +} + +float function DistanceAlongVector( vector origin, vector lineStart, vector lineForward ) +{ + vector originDif = origin - lineStart + return DotProduct( originDif, lineForward ) +} + +// NearestPointOnLine +vector function GetClosestPointOnLineSegment( vector a, vector b, vector p ) +{ + float distanceSqr = LengthSqr( a - b ) + + if ( distanceSqr == 0.0 ) + return a + + float t = DotProduct( p - a, b - a ) / distanceSqr + if ( t < 0.0 ) + return a + else if ( t > 1.0 ) + return b + + return a + t * (b - a) +} + +float function GetDistanceFromLineSegment( vector a, vector b, vector p ) +{ + vector closestPoint = GetClosestPointOnLineSegment( a, b, p ) + return Distance( p, closestPoint ) +} + +float function GetDistanceSqrFromLineSegment( vector a, vector b, vector p ) +{ + vector closestPoint = GetClosestPointOnLineSegment( a, b, p ) + return DistanceSqr( p, closestPoint ) +} + +float function GetProgressAlongLineSegment( vector P, vector A, vector B ) +{ + vector AP = P - A + vector AB = B - A + + float ab2 = DotProduct( AB, AB ) // AB.x*AB.x + AB.y*AB.y + float ap_ab = DotProduct( AP, AB ) // AP.x*AB.x + AP.y*AB.y + float t = ap_ab / ab2 + return t +} + +array function UntypedArrayToStringArray( array arr ) +{ + // work around for old arrays in code functions + array newArray + + for ( int i = 0; i < arr.len(); i++ ) + { + newArray.append( expect string( arr[i] ) ) + } + + return newArray +} + +string function PadString( string str, int len ) +{ + for ( int i = str.len(); i < len; i++ ) + str += " " + + return str +} + +bool function IsSpawner( entity ent ) +{ + return ( IsValid( ent ) && ent.GetClassName() == "spawner" ) +} + + + + +string function Dev_GetEnumString( table enumTable, int enumValue ) +{ + foreach ( string k, int v in enumTable ) + { + if ( v == enumValue ) + return k + } + + unreachable +} + +string function GetAllowedTitanAISettingsSearchString() +{ + if ( IsSingleplayer() ) + return "titanLoadoutInSP" + + return "mp_npcUseAllowed" +} + +string function GetAISettingsStringForMode() +{ + if ( IsSingleplayer() ) + return "sp_aiSettingsFile" + return "aiSettingsFile" +} + +array function GetAllowedPlayerTitanSettings() //Based off AllowedTItanAISettings, which is based off the titan_properties table +{ + string searchString + + if ( IsSingleplayer() ) + searchString = "titanLoadoutInSP" + else + searchString = "mp_npcUseAllowed" + + array list = [] + var dataTable = GetDataTable( $"datatable/titan_properties.rpak" ) + int numRows = GetDatatableRowCount( dataTable ) + + for ( int r=0; r function GetAllowedTitanAISettings( string settings = "" ) +{ + if ( settings == "" ) + { + settings = GetAISettingsStringForMode() + } + + string searchString = GetAllowedTitanAISettingsSearchString() + array list = [] + var dataTable = GetDataTable( $"datatable/titan_properties.rpak" ) + int numRows = GetDatatableRowCount( dataTable ) + + for ( int r=0; r function GetAllNPCSettings() +{ + // should be replaced with a getter that gets the content of classes.txt + array aiSettings + + // these npcs appear in the dev menu. Dev menu presence should be changed to a key in the settings file + aiSettings.append( "npc_drone" ) + aiSettings.append( "npc_drone_beam" ) + aiSettings.append( "npc_drone_plasma" ) + aiSettings.append( "npc_drone_worker" ) + aiSettings.append( "npc_dropship" ) + aiSettings.append( "npc_frag_drone" ) + aiSettings.append( "npc_frag_drone_throwable" ) + aiSettings.append( "npc_marvin" ) + aiSettings.append( "npc_prowler" ) + aiSettings.append( "npc_soldier" ) + aiSettings.append( "npc_soldier_hero_bear" ) + aiSettings.append( "npc_soldier_hero_sarah" ) + aiSettings.append( "npc_soldier_shield_captain" ) + aiSettings.append( "npc_soldier_sidearm" ) + aiSettings.append( "npc_soldier_specialist" ) + aiSettings.append( "npc_soldier_specialist_militia" ) + aiSettings.append( "npc_spectre" ) + aiSettings.append( "npc_stalker" ) + aiSettings.append( "npc_stalker_zombie" ) + aiSettings.append( "npc_stalker_zombie_mossy" ) + aiSettings.append( "npc_super_spectre" ) + aiSettings.append( "npc_titan_sarah" ) + aiSettings.append( "npc_titan_vanguard" ) + + // insert titans here! + aiSettings.extend( GetAllowedTitanAISettings() ) + + aiSettings.extend( [ + "npc_turret_mega" + "npc_turret_sentry" + "npc_turret_sentry_plasma" + "npc_turret_sentry_tday" + ] ) + + return aiSettings +} + +array function GetAllDeprecatedNPCSettings() +{ + // this is all the npc settings, then the legal settings are subtracted from it. + // also commented out settings that we don't want to have appear in the dev menu + table allNpcSettings = + { + //base_vehicle = true + //npc_bullseye = true + npc_drone = true + npc_drone_beam = true + npc_drone_cloaked = true + npc_drone_plasma = true + npc_drone_rocket = true + npc_drone_shield = true + npc_drone_worker = true + //npc_drone_plasma_fast = true + //npc_drone_rocket_fast = true + //npc_drone_worker_fast = true + npc_dropship = true + npc_dropship_dogfighter = true + npc_dropship_hero = true + npc_frag_drone = true + npc_frag_drone_throwable = true + npc_gunship = true + npc_gunship_scripted = true + npc_marvin = true + //npc_pilot_elite = true + npc_pilot_elite_assassin = true + npc_pilot_elite_assassin_cqb = true + npc_pilot_elite_assassin_sniper = true + //npc_pilot_elite_s2s = true + npc_prowler = true + npc_soldier = true + npc_soldier_bish = true + npc_soldier_blisk = true + npc_soldier_drone_summoner = true + npc_soldier_hero_bear = true + npc_soldier_hero_sarah = true + npc_soldier_shield_captain = true + npc_soldier_sidearm = true + npc_soldier_specialist = true + npc_soldier_specialist_militia = true + npc_soldier_spyglass = true + npc_soldier_training_sentry = true + npc_soldier_pve_sandbox = true + npc_soldier_pve_specialist = true + npc_soldier_pve_eliteguard = true + npc_spectre = true + npc_stalker = true + //npc_stalker_crawling = true + npc_stalker_zombie = true + npc_stalker_zombie_mossy = true + npc_super_spectre = true + npc_super_spectre_burnmeter = true + npc_super_spectre_calmer = true + npc_titan = true + npc_titan_arc = true + npc_titan_atlas = true + npc_titan_atlas_stickybomb = true + npc_titan_atlas_tracker = true + npc_titan_atlas_vanguard = true + npc_titan_auto = true + npc_titan_auto_atlas = true + npc_titan_auto_atlas_rocketeer = true + npc_titan_auto_atlas_stickybomb = true + npc_titan_auto_atlas_tracker = true + npc_titan_auto_atlas_vanguard = true + npc_titan_auto_ogre = true + npc_titan_auto_ogre_fighter = true + npc_titan_auto_ogre_meteor = true + npc_titan_auto_ogre_minigun = true + npc_titan_auto_stryder = true + npc_titan_auto_stryder_arc = true + npc_titan_auto_stryder_leadwall = true + npc_titan_auto_stryder_sniper = true + npc_titan_buddy = true + npc_titan_buddy_s2s = true + npc_titan_mortar = true + npc_titan_nuke = true + npc_titan_ogre = true + npc_titan_ogre_fighter = true + npc_titan_ogre_fighter_berserker_core = true + npc_titan_ogre_meteor = true + npc_titan_ogre_minigun = true + npc_titan_proto_stasisgun = true + npc_titan_sarah = true + npc_titan_vanguard = true + npc_titan_sniper = true + npc_titan_stryder = true + npc_titan_stryder_arc = true + npc_titan_stryder_leadwall = true + npc_titan_stryder_leadwall_shift_core = true + npc_titan_stryder_rocketeer = true + npc_titan_stryder_rocketeer_dash_core = true + npc_titan_stryder_sniper = true + npc_turret_mega = true + npc_turret_mega_nowindup = true + //npc_turret_mega_old = true + npc_turret_mega_windup = true + npc_turret_mega_attrition = true + npc_turret_mega_fortwar = true + npc_turret_sentry = true + npc_turret_sentry_plasma = true + npc_turret_sentry_burn_card_at = true + npc_turret_sentry_burn_card_ap = true + npc_turret_sentry_tactical_ability = true + npc_turret_sentry_tday = true + npc_turret_sentry_windup = true + npc_titan_atlas_stickybomb_bounty = true + npc_titan_atlas_tracker_bounty = true + npc_titan_atlas_vanguard_bounty = true + npc_titan_stryder_leadwall_bounty = true + npc_titan_stryder_sniper_bounty = true + npc_titan_ogre_meteor_bounty = true + npc_titan_ogre_minigun_bounty = true + } + + foreach ( aiSettings in GetAllNPCSettings() ) + { + delete allNpcSettings[ aiSettings ] + } + + array settings + foreach ( aiSettings, _ in allNpcSettings ) + { + settings.append( aiSettings ) + } + settings.sort( SortStringAlphabetize ) + + return settings +} + +/////// +// These are Brent's utility functions that match RUI functions of the same name; use these to accurately compute RUI values from script +float function EaseIn( float val ) +{ + return AttackDecay( 0, 2, val ) +} + +float function EaseOut( float val ) +{ + return AttackDecay( 2, 0, val ) +} + +float function AttackDecay( float attack, float decay, float time ) +{ + float sum = attack + decay + float a = sum - 2.0 + float b = (3.0 - attack) - sum + float c = attack + float t = max( min( time, 1.0 ), 0.0 ) + + return t * (c + t * (b + t * a)) +} + +float function Clamp( float value, float minValue, float maxValue ) +{ + return max( min( value, maxValue ), minValue ) +} +/////// + +string function GetCurrentPlaylistVarString( string varName, string defaultValue ) +{ + var value = GetCurrentPlaylistVar( varName ) + if ( value != null ) + return expect string( value ) + + return defaultValue +} + +bool function IsPlayerFemale( entity player ) +{ + return player.Dev_GetPlayerSettingByKeyField( "raceOrSex" ) == "race_human_female" +} + +bool function IsPlayerPrimeTitan( entity player ) +{ + if ( !player.IsTitan() ) + return false + + return player.Dev_GetPlayerSettingByKeyField( "isPrime" ) == 1 +} + +int function GetCollectibleLevelIndex( string mapName ) +{ + foreach( int i, table data in LEVEL_UNLOCKS_COUNT ) + { + if ( data.level == mapName ) + return i + } + return -1 +} + +int function GetFoundLionsInLevel( string mapName ) +{ + int saveIndex = GetCollectibleLevelIndex( mapName ) + Assert( saveIndex >= 0 ) + + return 0 +} + +int function GetMaxLionsInLevel( string mapName ) +{ + int saveIndex = GetCollectibleLevelIndex( mapName ) + Assert( saveIndex >= 0 ) + return expect int( LEVEL_UNLOCKS_COUNT[ saveIndex ].count ) +} + +int function GetCombinedLionsInLevel( string mapName ) +{ + int saveIndex = GetCollectibleLevelIndex( mapName ) + Assert( saveIndex >= 0 ) + + array bsps = GetBSPsForLevel( mapName ) + int count = 0 + foreach( string bsp in bsps ) + count += GetMaxLionsInLevel( bsp ) + + return count +} + +int function GetTotalLionsCollected() +{ + array bsps = GetAllR2SPBSPs() + int total + + foreach ( mapName in bsps ) + { + total += GetCollectiblesFoundForLevel( mapName ) + } + + return total +} + +int function GetTotalLionsInGame() +{ + array bsps = GetAllR2SPBSPs() + int total + + foreach ( mapName in bsps ) + { + total += GetMaxLionsInLevel( mapName ) + } + + return total +} + + + +int function GetCollectiblesFoundForLevel( string mapName ) +{ + int saveIndex = GetCollectibleLevelIndex( mapName ) + if ( saveIndex < 0 ) + return 0 + + int numCollectiblesFound = 0 + int numCollectiblesInLevel = GetMaxLionsInLevel( mapName ) + string unlockVar = "sp_unlocks_level_" + saveIndex + int bitMask = GetConVarInt( unlockVar ) + + for ( int i = 0 ; i < numCollectiblesInLevel ; i++ ) + { + int _id = 1 << i + if ( bitMask & _id ) + numCollectiblesFound++ + } + return numCollectiblesFound +} + +int function GetCombinedCollectiblesFoundForLevel( string mapName ) +{ +#if UI || CLIENT + if ( Script_IsRunningTrialVersion() ) + return 0 +#endif + + array bsps = GetBSPsForLevel( mapName ) + int count = 0 + foreach( string bsp in bsps ) + count += GetCollectiblesFoundForLevel( bsp ) + + return count +} + +void function ResetCollectiblesProgress_All() +{ + #if DEV + printt( "RESETTING COLLECTIBLE PROGRESS (ALL)" ) + #endif + + for ( int i = 0 ; i < 15 ; i++ ) + { + #if DEV + printt( " sp_unlocks_level_" + i, 0 ) + #endif + + SetConVarInt( "sp_unlocks_level_" + i, 0 ) + } +} + +void function RemoveDupesFromSorted_String( array data ) +{ + for ( int i = 0; i < data.len() - 1; i++ ) + { + if ( data[i] == data[i+1] ) + { + data.remove( i ) + i-- + } + } +} + + +function SortAlphabetize( a, b ) +{ + if ( a > b ) + return 1 + + if ( a < b ) + return -1 + + return 0 +} + +int function SortStringAlphabetize( string a, string b ) +{ + if ( a > b ) + return 1 + + if ( a < b ) + return -1 + + return 0 +} + +int function SortAssetAlphabetize( asset a, asset b ) +{ + if ( a > b ) + return 1 + + if ( a < b ) + return -1 + + return 0 +} + + +void function RemoveDupesFromSorted_Asset( array data ) +{ + for ( int i = 0; i < data.len() - 1; i++ ) + { + if ( data[i] == data[i+1] ) + { + data.remove( i ) + i-- + } + } +} + + +array function GetBSPsForLevel( string mapName ) +{ + array bsps + switch( mapName ) + { + // Levels aren't split, just return the bsp name of the level + case "sp_training": + case "sp_crashsite": + case "sp_sewers1": + case "sp_tday": + case "sp_s2s": + case "sp_skyway_v1": + case "sp_chadbox": + bsps.append( mapName ) + break + + // Have to return all bsps that are part of the level + case "sp_boomtown_start": + case "sp_boomtown": + case "sp_boomtown_end": + bsps.append( "sp_boomtown_start" ) + bsps.append( "sp_boomtown" ) + bsps.append( "sp_boomtown_end" ) + break + + case "sp_hub_timeshift": + case "sp_timeshift_spoke02": + bsps.append( "sp_hub_timeshift" ) + bsps.append( "sp_timeshift_spoke02" ) + break + + case "sp_beacon": + case "sp_beacon_spoke0": + bsps.append( "sp_beacon" ) + bsps.append( "sp_beacon_spoke0" ) + break + + default: + Assert( 0, "Unhandled collectible level " + mapName + " in GetBSPsForLevel" ) + break + } + + return bsps +} + +array function GetAllR2SPBSPs() +{ + return [ + "sp_training" + "sp_crashsite" + "sp_sewers1" + "sp_tday" + "sp_s2s" + "sp_skyway_v1" + "sp_boomtown_start" + "sp_boomtown" + "sp_boomtown_end" + "sp_hub_timeshift" + "sp_timeshift_spoke02" + "sp_beacon" + "sp_beacon_spoke0" ] +} + +//sp_unlocks_level + +asset function GetWeaponModel( string weaponName ) +{ + asset modelName = GetWeaponInfoFileKeyFieldAsset_Global( weaponName, "playermodel" ) + + return modelName +} + +asset function GetWeaponViewmodel( string weaponName ) +{ + asset modelName = GetWeaponInfoFileKeyFieldAsset_Global( weaponName, "viewmodel" ) + + // TODO: In the future all weapon models should be made in a way that doesn't require this kind of HACK. + switch ( modelName ) + { + case $"models/weapons/shoulder_rocket_SRAM/ptpov_law.mdl": + modelName = $"models/weapons/shoulder_rocket_SRAM/ptpov_law_menu.mdl" + break + + case $"models/weapons/lstar/ptpov_lstar.mdl": + modelName = $"models/weapons/lstar/ptpov_lstar_menu.mdl" + break + + case $"models/weapons/softball_at/ptpov_softball_at.mdl": + modelName = $"models/weapons/softball_at/ptpov_softball_at_menu.mdl" + break + + case $"models/weapons/mastiff_stgn/ptpov_mastiff.mdl": + modelName = $"models/weapons/mastiff_stgn/ptpov_mastiff_menu.mdl" + break + } + + return modelName +} + +string function GetTitanClassFromSetFile( string setFile ) //JFS: As of right now titanClass is really just the same thing as "titanCharacterName". Should change this for next game if needed. +{ + var result = Dev_GetPlayerSettingByKeyField_Global( setFile, "titanCharacterName" ) + expect string( result ) + return result +} + + +string function GetTitanCharacterNameFromSetFile( string setFile ) +{ + var result = Dev_GetPlayerSettingByKeyField_Global( setFile, "titanCharacterName" ) + if ( result == null ) + { + CodeWarning( "Warning, no field titanCharacterName found for set file: " + setFile + ", returning default of ion" ) + return "ion" + } + + expect string( result ) + return result +} + + +string function GetTitanReadyMessageFromSetFile( string setFile ) +{ + var result = Dev_GetPlayerSettingByKeyField_Global( setFile, "readymessage" ) + if ( result == null ) + { + CodeWarning( "Warning, no field readymessage found for set file: " + setFile ) + return "ion" + } + + expect string( result ) + return result +} + + + + +#if CLIENT || UI +void function DisablePrecacheErrors() +{ + file.lastHostThreadMode = GetConVarInt( "host_thread_mode" ) + file.lastScriptPrecacheErrors = GetConVarInt( "script_precache_errors" ) + file.lastReportFatal = GetConVarInt( "fs_report_sync_opens_fatal" ) + + #if CLIENT + entity player = GetLocalClientPlayer() + player.ClientCommand( "host_thread_mode 0" ) + player.ClientCommand( "script_precache_errors 0" ) + player.ClientCommand( "fs_report_sync_opens_fatal 0" ) + #endif + + #if UI + ClientCommand( "host_thread_mode 0" ) + ClientCommand( "script_precache_errors 0" ) + ClientCommand( "fs_report_sync_opens_fatal 0" ) + #endif +} + +void function RestorePrecacheErrors() +{ + #if CLIENT + entity player = GetLocalClientPlayer() + player.ClientCommand( "host_thread_mode " + file.lastHostThreadMode ) + player.ClientCommand( "script_precache_errors " + file.lastScriptPrecacheErrors ) + player.ClientCommand( "fs_report_sync_opens_fatal " + file.lastReportFatal ) + #endif + + #if UI + ClientCommand( "host_thread_mode " + file.lastHostThreadMode ) + ClientCommand( "script_precache_errors " + file.lastScriptPrecacheErrors ) + ClientCommand( "fs_report_sync_opens_fatal " + file.lastReportFatal ) + #endif +} +#endif + +#if SERVER +void function DisablePrecacheErrors() +{ + foreach ( player in GetPlayerArray() ) + { + Remote_CallFunction_UI( player, "DisablePrecacheErrors" ) + } +} + +void function RestorePrecacheErrors() +{ + foreach ( player in GetPlayerArray() ) + { + Remote_CallFunction_UI( player, "RestorePrecacheErrors" ) + } +} + +#endif + +string function GetTitanReadyHintFromSetFile( string setFile ) +{ + var result = Dev_GetPlayerSettingByKeyField_Global( setFile, "readyhint" ) + if ( result == null ) + { + CodeWarning( "Warning, no field readyhint found for set file: " + setFile ) + return "" + } + + expect string( result ) + return result +} + + +void function HideHudElem( var hudelem ) +{ + hudelem.Hide() // sp does not run as much logic, so these elems don't get hidden on player death/spawn. +} + +void function ShowHudElem( var hudelem ) +{ + hudelem.Show() +} + +int function GetBestCompletedDifficultyForLevelId( string uniqueIdentifier ) +{ + int bspNum = GetBSPNum( uniqueIdentifier ) + + if ( GetCompletedDifficultyForBSPNum( bspNum, "sp_missionMasterCompletion" ) ) + return DIFFICULTY_MASTER + if ( GetCompletedDifficultyForBSPNum( bspNum, "sp_missionHardCompletion" ) ) + return DIFFICULTY_HARD + if ( GetCompletedDifficultyForBSPNum( bspNum, "sp_missionNormalCompletion" ) ) + return DIFFICULTY_NORMAL + if ( GetCompletedDifficultyForBSPNum( bspNum, "sp_missionEasyCompletion" ) ) + return DIFFICULTY_EASY + + return -1 // not completed +} + +bool function GetCompletedDifficultyForLevelId( string uniqueIdentifier, string pvar ) +{ + int bspNum = GetBSPNum( uniqueIdentifier ) + + Assert ( bspNum >= 0 ) + + return GetCompletedDifficultyForBSPNum( bspNum, pvar ) +} + +bool function DevStartPoints() +{ + #if DEV + return true + #endif + + return Dev_CommandLineHasParm( "-startpoints" ) +} + +int function GetBSPNum( string uniqueIdentifier ) +{ + var dataTable = GetDataTable( $"datatable/sp_levels.rpak" ) + int numRows = GetDatatableRowCount( dataTable ) + int bspCol = GetDataTableColumnByName( dataTable, "levelId" ) + + for ( int i = 0; i = 1 ) +} + +int function GetLastLevelUnlocked() +{ + if ( file.devUnlockedSPMissions ) + { + return 9 + } + return GetConVarInt( "sp_unlockedMission" ) +} + +void function UnlockSPLocally() +{ + file.devUnlockedSPMissions = true +} + +bool function CoinFlip() +{ + return RandomInt( 2 ) != 0 +} + +bool function EmotesEnabled() +{ + return false +} + + +array function GetAvailableTitanRefs( entity player ) +{ + array availableTitanRefs + + int enumCount = PersistenceGetEnumCount( "titanClasses" ) + for ( int i = 0; i < enumCount; i++ ) + { + string enumName = PersistenceGetEnumItemNameForIndex( "titanClasses", i ) + if ( enumName == "" ) + continue + + if ( player.GetPersistentVarAsInt( "titanClassLockState[" + enumName + "]" ) > TITAN_CLASS_LOCK_STATE_AVAILABLE ) + continue + + availableTitanRefs.append( enumName ) + } + + return availableTitanRefs +} + +#if MP +string function GetTitanRefForLoadoutIndex( entity player, int loadoutIndex ) +{ + TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex ) + return loadout.titanClass +} +#endif + +bool function DoPrematchWarpSound() +{ + return GetCurrentPlaylistVarInt( "pick_loadout_warp_sound", 1 ) == 1 +} + + +int function GetIntFromString( string inString ) +{ + if ( inString.len() > 0 ) + { + string firstChar = inString.slice( 0, 1 ) + if ( ( firstChar >= "0" && firstChar <= "9" ) || firstChar == "." ) + return int( inString ) + else + return expect int( getconsttable()[ inString ] ) + } + + return 0 +} + +//Function returns whether the given mode name is a Frontier Defense mode. +bool function IsFDMode( string modeName ) +{ + bool isFD = false + switch ( modeName ) + { + case "fd_easy": + case "fd_normal": + case "fd_hard": + case "fd_master": + case "fd_insane": + isFD = true + } + + return isFD +} + +// Join an array of strings with a seperator +string function JoinStringArray( array strings, string separator ) +{ + string output; + + foreach( int i, string stringoStarr in strings ) + { + if (i == 0) + output = stringoStarr + else + output = output + separator + stringoStarr + } + + return output; +} \ No newline at end of file -- cgit v1.2.3