aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/mod/scripts/vscripts
diff options
context:
space:
mode:
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts')
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut3
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut20
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut15
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut1
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut5
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut8
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut32
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut4
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut7
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut4061
10 files changed, 4137 insertions, 19 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut
index 1feefc2b..02be47a4 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut
@@ -35,7 +35,8 @@ void function WritePersistenceAndLeaveForLocalPlayerOnly( entity player )
while ( GetPlayerArray().len() != 1 && Time() < time + 5.0 )
WaitFrame()
- WritePersistenceAndLeave( player )
+ if ( IsValid( player ) )
+ WritePersistenceAndLeave( player )
}
void function WritePersistenceAndLeave( entity player )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
index 4be04643..3151a0f2 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut
@@ -49,6 +49,7 @@ void function BurnMeter_Init()
AddCallback_OnClientConnected( InitBurncardsForLateJoiner )
AddCallback_OnPlayerRespawned( StartPhaseRewindLifetime )
+ AddCallback_OnTitanBecomesPilot( RemoveAmpedWeaponsForTitanPilot )
// necessary signals
RegisterSignal( "StopAmpedWeapons" )
@@ -241,7 +242,17 @@ void function PlayerUsesAmpedWeaponsBurncardThreaded( entity player )
{
weapon.RemoveMod( "silencer" ) // both this and the burnmod will override firing fx, if a second one overrides this we crash
foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) )
- weapon.AddMod( mod )
+ {
+ // catch incompatibilities just in case
+ try
+ {
+ weapon.AddMod( mod )
+ }
+ catch( ex )
+ {
+ weapons.removebyvalue( weapon )
+ }
+ }
// needed to display amped weapon time left
weapon.SetScriptFlags0( weapon.GetScriptFlags0() | WEAPONFLAG_AMPED )
@@ -263,6 +274,13 @@ void function PlayerUsesAmpedWeaponsBurncardThreaded( entity player )
}
}
+void function RemoveAmpedWeaponsForTitanPilot( entity player, entity titan )
+{
+ foreach ( entity weapon in player.GetMainWeapons() )
+ foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) )
+ weapon.RemoveMod( mod )
+}
+
void function PlayerUsesSmartPistolBurncard( entity player )
{
// take secondary weapon
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
index a4f23b85..b861ed9f 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut
@@ -103,11 +103,20 @@ void function EvacEpilogue()
if ( canRunEvac )
{
- SetRespawnsEnabled( false )
+ thread SetRespawnAndWait( false )
thread Evac( GetOtherTeam( winner ), EVAC_INITIAL_WAIT, EVAC_ARRIVAL_TIME, EVAC_WAIT_TIME, EvacEpiloguePlayerCanBoard, EvacEpilogueShouldLeaveEarly, EvacEpilogueCompleted )
}
else
- thread EvacEpilogueCompleted( null ) // this is hacky but like, this also shouldn't really be hit in normal gameplay
+ {
+ thread SetRespawnAndWait( false ) //prevent respawns during the fade to black, should only be an issue if the match is a draw
+ thread EvacEpilogueCompleted( null ) //this is hacky but like, this also shouldn't really be hit in normal gameplay
+ }
+}
+
+void function SetRespawnAndWait(bool mode)
+{
+ wait GAME_EPILOGUE_PLAYER_RESPAWN_LEEWAY
+ SetRespawnsEnabled( mode )
}
bool function EvacEpiloguePlayerCanBoard( entity dropship, entity player )
@@ -386,4 +395,4 @@ void function EvacDropshipKilled( entity dropship, var damageInfo )
player.Die( DamageInfo_GetAttacker( damageInfo ), DamageInfo_GetWeapon( damageInfo ), { damageSourceId = eDamageSourceId.evac_dropship_explosion, scriptType = DF_GIB } )
}
}
-} \ No newline at end of file
+}
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
index 7f879c69..dc976059 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -327,6 +327,7 @@ void function GiveFlag( entity player, entity flag )
void function DropFlagIfPhased( entity player, entity flag )
{
player.EndSignal( "StartPhaseShift" )
+ player.EndSignal( "OnDestroy" )
OnThreadEnd( function() : ( player )
{
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
index 49fd5f2e..22c660d8 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut
@@ -86,6 +86,7 @@ void function GiveFlag( entity player )
void function DropFlagIfPhased( entity player )
{
player.EndSignal( "StartPhaseShift" )
+ player.EndSignal( "OnDestroy" )
OnThreadEnd( function() : ( player )
{
@@ -102,7 +103,9 @@ void function DropFlag()
file.flag.ClearParent()
file.flag.SetAngles( < 0, 0, 0 > )
SetGlobalNetEnt( "flagCarrier", file.flag )
- EmitSoundOnEntityOnlyToPlayer( file.flagCarrier, file.flagCarrier, "UI_CTF_1P_FlagDrop" )
+
+ if ( IsValid( file.flagCarrier ) )
+ EmitSoundOnEntityOnlyToPlayer( file.flagCarrier, file.flagCarrier, "UI_CTF_1P_FlagDrop" )
foreach ( entity player in GetPlayerArray() )
MessageToPlayer( player, eEventNotifications.SPEEDBALL_FlagDropped, file.flagCarrier )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
index 08cc9d0b..388f4794 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut
@@ -37,6 +37,10 @@ void function SetupPrivateMatchUIVarsWhenReady()
bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<string> args )
{
+ if ( GetConVarBool( "ns_private_match_only_host_can_start" ) )
+ if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
+ return true
+
if ( file.startState == ePrivateMatchStartState.STARTING )
{
// cancel start if we're already mid-countdown
@@ -46,10 +50,6 @@ bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<str
}
else
{
- if ( GetConVarBool( "ns_private_match_only_host_can_start" ) )
- if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) )
- return true
-
// start match
file.startState = ePrivateMatchStartState.STARTING
thread StartMatch()
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
index 4f6a1291..d3b4cd7e 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut
@@ -64,25 +64,40 @@ void function OnPrematchStart()
// make 2 empty dropship structs per team
IntroDropship emptyDropship
- file.militiaDropships = [ clone emptyDropship, clone emptyDropship ]
- file.imcDropships = [ clone emptyDropship, clone emptyDropship ]
+ file.militiaDropships.clear()
+ file.imcDropships.clear()
- // spawn dropships
- foreach ( entity dropshipSpawn in GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" ) )
- {
+ array<entity> validDropshipSpawns
+ array<entity> dropshipSpawns = GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" )
+ foreach ( entity dropshipSpawn in dropshipSpawns )
+ {
if ( dropshipSpawn.HasKey( "gamemode_" + GetSpawnpointGamemodeOverride() ) )
if ( dropshipSpawn.kv[ "gamemode_" + GetSpawnpointGamemodeOverride() ] == "0" )
continue
+
+ validDropshipSpawns.append( dropshipSpawn )
+ }
+
+ // if no dropship spawns for this mode, just allow any dropship spawns
+ if ( validDropshipSpawns.len() < 2 )
+ validDropshipSpawns = dropshipSpawns
+ // spawn dropships
+ foreach ( entity dropshipSpawn in validDropshipSpawns )
+ {
// todo: possibly make this only spawn dropships if we've got enough players to need them
int createTeam = HasSwitchedSides() ? dropshipSpawn.GetTeam() : GetOtherTeam( dropshipSpawn.GetTeam() )
array<IntroDropship> teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships
- int dropshipIndex = !IsValid( teamDropships[ 0 ].dropship ) ? 0 : 1
+ if ( teamDropships.len() >= 2 )
+ continue
+
// create entity
entity dropship = CreateDropship( createTeam, dropshipSpawn.GetOrigin(), dropshipSpawn.GetAngles() )
- teamDropships[ dropshipIndex ].dropship = dropship
+ teamDropships.append( clone emptyDropship )
+ teamDropships[ teamDropships.len() - 1 ].dropship = dropship
+
AddAnimEvent( dropship, "dropship_warpout", WarpoutEffect )
dropship.SetValueForModelKey( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" )
@@ -110,6 +125,8 @@ void function EndIntroWhenFinished()
void function SpawnPlayerIntoDropship( entity player )
{
+ player.EndSignal( "OnDestroy" )
+
if ( IsAlive( player ) )
player.Die() // kill them so we don't have any issues respawning them later
@@ -133,7 +150,6 @@ void function SpawnPlayerIntoDropship( entity player )
WaitFrame()
player.EndSignal( "OnDeath" )
- player.EndSignal( "OnDestroy" )
// find the player's dropship and seat
array<IntroDropship> teamDropships
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
index 2f16379e..63ecbf68 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut
@@ -183,6 +183,10 @@ void function GameStateEnter_Prematch()
void function StartGameWithoutClassicMP()
{
+ foreach ( entity player in GetPlayerArray() )
+ if ( IsAlive( player ) )
+ player.Die()
+
WaitFrame() // wait for callbacks to finish
// need these otherwise game will complain
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut
index 7b126cd0..c84e6aba 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut
@@ -178,7 +178,12 @@ void function GiveWeaponsFromStoredArray( entity player, array<StoredWeapon> sto
UpdateProScreen( player, weapon )
}
- string weaponCategory = GetWeaponInfoFileKeyField_GlobalString( weapon.GetWeaponClassName(), "menu_category" )
+ string weaponCategory = ""
+ if ( IsWeaponKeyFieldDefined(weapon.GetWeaponClassName(), "menu_category") )
+ {
+ weaponCategory = GetWeaponInfoFileKeyField_GlobalString( weapon.GetWeaponClassName(), "menu_category" )
+ }
+
if ( weaponCategory == "at" || weaponCategory == "special" ) // refill AT/grenadier ammo stockpile
{
int defaultTotal = weapon.GetWeaponSettingInt( eWeaponVar.ammo_default_total )
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut b/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut
new file mode 100644
index 00000000..d15220e4
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut
@@ -0,0 +1,4061 @@
+#if SERVER
+untyped
+#endif
+
+globalize_all_functions
+
+global string INVALID_REF = "INVALID_REF"
+
+void function InitDefaultLoadouts()
+{
+ PopulateDefaultPilotLoadouts( shGlobal.defaultPilotLoadouts )
+ PopulateDefaultTitanLoadouts( shGlobal.defaultTitanLoadouts )
+}
+
+PilotLoadoutDef function GetDefaultPilotLoadout( int index )
+{
+ return shGlobal.defaultPilotLoadouts[ index ]
+}
+
+TitanLoadoutDef function GetDefaultTitanLoadout( int index )
+{
+ return shGlobal.defaultTitanLoadouts[ index ]
+}
+
+PilotLoadoutDef[NUM_PERSISTENT_PILOT_LOADOUTS] function GetDefaultPilotLoadouts()
+{
+ return shGlobal.defaultPilotLoadouts
+}
+
+TitanLoadoutDef[NUM_PERSISTENT_TITAN_LOADOUTS] function GetDefaultTitanLoadouts()
+{
+ return shGlobal.defaultTitanLoadouts
+}
+
+void function PopulateDefaultPilotLoadouts( PilotLoadoutDef[ NUM_PERSISTENT_PILOT_LOADOUTS ] loadouts )
+{
+ var dataTable = GetDataTable( $"datatable/default_pilot_loadouts.rpak" )
+
+ for ( int i = 0; i < NUM_PERSISTENT_PILOT_LOADOUTS; i++ )
+ {
+ PilotLoadoutDef loadout = loadouts[i]
+ loadout.name = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "name" ) )
+ loadout.suit = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "suit" ) )
+ loadout.race = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "race" ) )
+ loadout.primary = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primary" ) )
+ loadout.primaryAttachment = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryAttachment" ) )
+ loadout.primaryMod1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod1" ) )
+ loadout.primaryMod2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod2" ) )
+ loadout.primaryMod3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod3" ) )
+ loadout.secondary = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondary" ) )
+ loadout.secondaryMod1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondaryMod1" ) )
+ loadout.secondaryMod2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondaryMod2" ) )
+ loadout.secondaryMod3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondaryMod3" ) )
+ loadout.weapon3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3" ) )
+ loadout.weapon3Mod1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3Mod1" ) )
+ loadout.weapon3Mod2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3Mod2" ) )
+ loadout.weapon3Mod3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3Mod3" ) )
+ loadout.ordnance = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "ordnance" ) )
+ loadout.passive1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive1" ) )
+ loadout.passive2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive2" ) )
+
+ //TODO: Why isn't execution initialized here?
+
+ UpdateDerivedPilotLoadoutData( loadout, false )
+
+ //loadout.race
+ //ValidateDefaultLoadoutData( "pilot", loadout.primary )
+ //ValidateWeaponSubitem( loadout.primary, loadout.primaryAttachment, eItemTypes.PILOT_PRIMARY_ATTACHMENT )
+ //ValidateWeaponSubitem( loadout.primary, loadout.primaryMod1, eItemTypes.PILOT_PRIMARY_MOD )
+ //ValidateWeaponSubitem( loadout.primary, loadout.primaryMod2, eItemTypes.PILOT_PRIMARY_MOD )
+ Assert( ( loadout.primaryMod1 == "" && loadout.primaryMod2 == "" ) || ( loadout.primaryMod1 != loadout.primaryMod2 ), "!!! Primary mod1 and mod2 in default pilot loadout: " + loadout.name + " should be different but are both set to: " + loadout.primaryMod1 )
+
+ //ValidateDefaultLoadoutData( "pilot", loadout.secondary )
+ //ValidateWeaponSubitem( loadout.secondary, loadout.secondaryMod1, eItemTypes.PILOT_SECONDARY_MOD )
+ //ValidateWeaponSubitem( loadout.secondary, loadout.secondaryMod2, eItemTypes.PILOT_SECONDARY_MOD )
+ Assert( ( loadout.secondaryMod1 == "" && loadout.secondaryMod2 == "" ) || ( loadout.secondaryMod1 != loadout.secondaryMod2 ), "!!! Secondary mod1 and mod2 in default pilot loadout: " + loadout.name + " should be different but are both set to: " + loadout.secondaryMod1 )
+
+ //ValidateDefaultLoadoutData( "pilot", loadout.weapon3 )
+ //ValidateWeaponSubitem( loadout.weapon3, loadout.weapon3Mod1, eItemTypes.PILOT_SECONDARY_MOD )
+ //ValidateWeaponSubitem( loadout.weapon3, loadout.weapon3Mod2, eItemTypes.PILOT_SECONDARY_MOD )
+ Assert( ( loadout.weapon3Mod1 == "" && loadout.weapon3Mod2 == "" ) || ( loadout.weapon3Mod1 != loadout.weapon3Mod2 ), "!!! Weapon3 mod1 and mod2 in default pilot loadout: " + loadout.name + " should be different but are both set to: " + loadout.weapon3Mod1 )
+
+ //ValidateDefaultLoadoutData( "pilot", loadout.ordnance )
+ //ValidateDefaultLoadoutData( "pilot", loadout.special )
+ //ValidateDefaultLoadoutData( "pilot", loadout.passive1 )
+ //ValidateDefaultLoadoutData( "pilot", loadout.passive2 )
+ }
+}
+
+void function PopulateDefaultTitanLoadouts( TitanLoadoutDef[ NUM_PERSISTENT_TITAN_LOADOUTS ] loadouts )
+{
+ var dataTable = GetDataTable( $"datatable/default_titan_loadouts.rpak" )
+
+ for ( int i = 0; i < NUM_PERSISTENT_TITAN_LOADOUTS; i++ )
+ {
+ TitanLoadoutDef loadout = loadouts[i]
+ loadout.name = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "name" ) )
+ loadout.titanClass = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "titanRef" ) )
+ loadout.setFile = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "setFile" ) )
+ loadout.primaryMod = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod" ) )
+ loadout.special = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "special" ) )
+ loadout.antirodeo = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "antirodeo" ) )
+ loadout.passive1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive1" ) )
+ loadout.passive2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive2" ) )
+ loadout.passive3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive3" ) )
+ loadout.passive4 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive4" ) )
+ loadout.passive5 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive5" ) )
+ loadout.passive6 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive6" ) )
+ loadout.titanExecution = GetLoadoutPropertyDefault( "titan", i, "titanExecution" )
+
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout )
+
+ //ValidateDefaultLoadoutData( "titan", loadout.setFile )
+ ////loadout.primaryMod
+ //ValidateDefaultLoadoutData( "titan", loadout.special )
+ //ValidateDefaultLoadoutData( "titan", loadout.antirodeo )
+ //ValidateDefaultLoadoutData( "titan", loadout.passive1 )
+ //ValidateDefaultLoadoutData( "titan", loadout.passive2 )
+ }
+}
+
+void function ValidateDefaultLoadoutData( string loadoutType, string itemRef )
+{
+ if ( itemRef == "" )
+ return
+
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ string tableFile = "default_" + loadoutType + "_loadouts.csv"
+
+ Assert( ItemDefined( itemRef ), "Datatable \"" + tableFile + "\" contains an unknown item reference: " + itemRef )
+ Assert( GetUnlockLevelReq( itemRef ) <= 1, "Datatable \"" + tableFile + "\" item: " + itemRef + " must be unlocked at level 1" )
+}
+
+void function ValidateWeaponSubitem( string weaponRef, string itemRef, int itemType )
+{
+ bool isPlayerLevelLocked = IsItemLockedForPlayerLevel( 1, weaponRef )
+ bool isWeaponLevelLocked = IsItemLockedForWeaponLevel( 1, weaponRef, itemRef )
+
+ if ( isPlayerLevelLocked || isWeaponLevelLocked )
+ {
+ //array<ItemData> subitems = GetAllSubitemsOfType( weaponRef, itemType )
+ //foreach ( subitem in subitems )
+ //{
+ // if ( !IsItemLockedForWeaponLevel( 1, weaponRef, subitem.ref ) )
+ // printt( " ", subitem.ref )
+ //}
+
+ Assert( 0, "Subitem: " + itemRef + " for item: " + weaponRef + " should either be available by default, or changed to one of the values listed above." )
+ CodeWarning( "Subitem: " + itemRef + " for item: " + weaponRef + " should either be available by default, or changed to one of the values listed above." )
+ }
+}
+
+PilotLoadoutDef function GetPilotLoadoutFromPersistentData( entity player, int loadoutIndex )
+{
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout
+}
+
+TitanLoadoutDef function GetTitanLoadoutFromPersistentData( entity player, int loadoutIndex )
+{
+ TitanLoadoutDef loadout
+ PopulateTitanLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout
+}
+
+void function PopulatePilotLoadoutFromPersistentData( entity player, PilotLoadoutDef loadout, int loadoutIndex )
+{
+ loadout.name = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "name" )
+ loadout.suit = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "suit" )
+ loadout.race = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "race" )
+ loadout.execution = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "execution" )
+ loadout.primary = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primary" )
+ loadout.primaryAttachment = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryAttachment" )
+ loadout.primaryMod1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod1" )
+ loadout.primaryMod2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod2" )
+ loadout.primaryMod3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod3" )
+ loadout.secondary = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondary" )
+ loadout.secondaryMod1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod1" )
+ loadout.secondaryMod2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod2" )
+ loadout.secondaryMod3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod3" )
+ loadout.weapon3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3" )
+ loadout.weapon3Mod1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod1" )
+ loadout.weapon3Mod2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod2" )
+ loadout.weapon3Mod3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod3" )
+ loadout.ordnance = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "ordnance" )
+ loadout.passive1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive1" )
+ loadout.passive2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive2" )
+ loadout.camoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "camoIndex" )
+ loadout.skinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "skinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.primaryCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "primaryCamoIndex" )
+ loadout.primarySkinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "primarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.secondaryCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "secondaryCamoIndex" )
+ loadout.secondarySkinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "secondarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.weapon3CamoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "weapon3CamoIndex" )
+ loadout.weapon3SkinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "weapon3SkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+
+ UpdateDerivedPilotLoadoutData( loadout )
+}
+
+void function PopulateTitanLoadoutFromPersistentData( entity player, TitanLoadoutDef loadout, int loadoutIndex )
+{
+ loadout.name = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "name" )
+ loadout.titanClass = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass" )
+ loadout.primaryMod = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "primaryMod" )
+ loadout.special = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "special" )
+ loadout.antirodeo = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "antirodeo" )
+ loadout.passive1 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive1" )
+ loadout.passive2 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive2" )
+ loadout.passive3 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive3" )
+ loadout.passive4 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive4" )
+ loadout.passive5 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive5" )
+ loadout.passive6 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive6" )
+ loadout.camoIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "camoIndex" )
+ loadout.skinIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "skinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.decalIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "decalIndex" )
+ loadout.primaryCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primaryCamoIndex" )
+ loadout.primarySkinIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.titanExecution = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "titanExecution" )
+ loadout.showArmBadge = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "showArmBadge" )
+
+ //Prime Titan related vars
+ loadout.isPrime = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "isPrime" )
+ loadout.primeCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeCamoIndex" )
+ loadout.primeSkinIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeSkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.primeDecalIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeDecalIndex" )
+
+ UpdateDerivedTitanLoadoutData( loadout )
+ OverwriteLoadoutWithDefaultsForSetFile( loadout )
+}
+
+string function GetSetFileForTitanClassAndPrimeStatus( string titanClass, bool isPrimeTitan = false )
+{
+ string nonPrimeSetFile
+
+ array<TitanLoadoutDef> legalLoadouts = GetAllowedTitanLoadouts()
+
+ foreach ( loadout in legalLoadouts )
+ {
+ if ( GetTitanCharacterNameFromSetFile( loadout.setFile ) == titanClass )
+ {
+ nonPrimeSetFile = loadout.setFile
+ break
+ }
+ }
+
+ if ( !isPrimeTitan )
+ return nonPrimeSetFile
+
+ string primeSetFile = GetPrimeTitanSetFileFromNonPrimeSetFile( nonPrimeSetFile )
+ Assert( primeSetFile != "" )
+ return primeSetFile
+}
+
+
+string function GetPrimeTitanRefForTitanClass( string titanClass )
+{
+ array<TitanLoadoutDef> legalLoadouts = GetAllowedTitanLoadouts()
+
+ foreach ( loadout in legalLoadouts )
+ {
+ if ( GetTitanCharacterNameFromSetFile( loadout.setFile ) == titanClass )
+ return loadout.primeTitanRef
+ }
+
+ unreachable
+}
+
+string function GetPersistentLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ // printt( "=======================================================================================" )
+ // printt( "loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty )
+ // printl( "script GetPlayerArray()[0].SetPersistentVar( \"" + loadoutType + "Loadouts[" + loadoutIndex + "]." + loadoutProperty + "\", \"" + value + "\" )" )
+ // printt( "=======================================================================================" )
+
+ string getString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, loadoutProperty )
+ var value = player.GetPersistentVar( getString )
+
+ if ( value == null )
+ return ""
+
+ return string( value )
+}
+
+
+int function GetPersistentLoadoutValueInt( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ return int( GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, loadoutProperty ) )
+}
+
+string function GetPersistentLoadoutPropertyType( string loadoutProperty )
+{
+ switch ( loadoutProperty )
+ {
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "showArmBadge":
+ return "int"
+ }
+
+ return "string"
+}
+
+
+#if SERVER
+// TODO: If we change a property that has a parent or child relationship, all related properties need updating if invalid
+// A parent change should validate children and set invalid to defaults
+// If a child change is invalid for the parent, it should be changed to a valid default based on the parent
+// TODO: Return type. Currently getting passed to AddClientCommandCallback() which expects a bool return, but this isn't setup to return values.
+function SetPersistentLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string value )
+{
+ //printt( "=======================================================================================" )
+ //printt( "SetPersistentLoadoutValue called with loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty, "value:", value )
+ //printl( "script GetPlayerArray()[0].SetPersistentVar( \"" + loadoutType + "Loadouts[" + loadoutIndex + "]." + loadoutProperty + "\", \"" + value + "\" )" )
+ //printt( "=======================================================================================" )
+
+ bool loadoutIsPilot = ( loadoutType == "pilot" )
+ bool loadoutIsTitan = ( loadoutType == "titan" )
+ bool loadoutIsPilotOrTitan = ( loadoutIsPilot || loadoutIsTitan )
+
+ //Assert( loadoutIsPilotOrTitan, "Invalid loadoutType that is not pilot or titan" )
+ if ( !loadoutIsPilotOrTitan )
+ {
+ CodeWarning( "Loadout neither pilot or titan" )
+ return
+ }
+
+ //Assert( ( loadoutIsPilot && IsValidPilotLoadoutIndex( loadoutIndex ) ) || ( loadoutIsTitan && !IsValidTitanLoadoutIndex( loadoutIndex ) ), "SetPersistentLoadoutValue() called with invalid loadoutIndex: " + loadoutIndex )
+
+ if ( loadoutIsPilot && !IsValidPilotLoadoutIndex( loadoutIndex ) )
+ {
+ CodeWarning( "!IsValidPilotLoadoutIndex" )
+ return
+ }
+
+ if ( loadoutIsTitan && !IsValidTitanLoadoutIndex( loadoutIndex ) )
+ {
+ CodeWarning( "!IsValidTitanLoadoutIndex" )
+ return
+ }
+
+ if ( value == "none" )
+ value = ""
+
+ var loadoutPropertyEnum = null
+ if ( loadoutIsPilot )
+ {
+ bool isValid = IsValidPilotLoadoutProperty( loadoutProperty )
+ //Assert( IsValidPilotLoadoutProperty, "SetPersistentLoadoutValue() called with invalid pilot loadoutProperty: " + loadoutProperty )
+ if ( !isValid )
+ {
+ CodeWarning( "!IsValidPilotLoadoutProperty" )
+ return
+ }
+
+ loadoutPropertyEnum = GetPilotLoadoutPropertyEnum( loadoutProperty )
+ }
+ else
+ {
+ bool isValid = IsValidTitanLoadoutProperty( loadoutProperty )
+ //Assert( isValidTitanLoadoutProperty, "SetPersistentLoadoutValue() called with invalid titan loadoutProperty: " + loadoutProperty )
+ if ( !isValid )
+ {
+ CodeWarning( "!IsValidTitanLoadoutProperty" )
+ return
+ }
+
+ loadoutPropertyEnum = GetTitanLoadoutPropertyEnum( loadoutProperty )
+ }
+
+ if ( loadoutPropertyEnum != null && !PersistenceEnumValueIsValid( loadoutPropertyEnum, value ) )
+ {
+ CodeWarning( loadoutType + " " + loadoutProperty + " value: " + value + " not valid in persistent data!" )
+ return
+ }
+
+ if ( LoadoutPropertyRequiresItemValidation( loadoutProperty ) && value != "" )
+ {
+ if ( FailsLoadoutValidationCheck( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ printt( "=======================================================================================" )
+ printt( "SetPersistentLoadoutValue failed FailsLoadoutValidationCheck with loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty, "value:", value )
+ printt( "=======================================================================================" )
+
+ //Assert( false, "SetPersistentLoadoutValue() call " + loadoutType + " " + loadoutProperty + ", value: " + value + " failed FailsLoadoutValidationCheck() check!" )
+ return
+ }
+ }
+
+ // Only checks primary mods and attachments are valid in itemData and the parent isn't locked
+ if ( !IsLoadoutSubitemValid( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ CodeWarning( loadoutType + " " + loadoutProperty + " value: " + value + " failed IsLoadoutSubitemValid() check! It may not exist in itemData, or it's parent item is locked." )
+ return
+ }
+
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, loadoutProperty, value )
+
+ // Reset child properties when parent changes
+ ResolveInvalidLoadoutChildValues( player, loadoutType, loadoutIndex, loadoutProperty, value )
+ ValidateSkinAndCamoIndexesAsAPair( player, loadoutType, loadoutIndex, loadoutProperty, value ) //Skin and camo properties need to be set correctly as a pair
+
+ #if HAS_THREAT_SCOPE_SLOT_LOCK
+ if ( loadoutProperty.tolower() == "primaryattachment" && value == "threat_scope" )
+ {
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, "primaryMod2", "" )
+ }
+ #endif
+
+ // TEMP client model update method
+ bool updateModel = ( loadoutProperty == "suit" || loadoutProperty == "race" || loadoutProperty == "setFile" || loadoutProperty == "primary" || loadoutProperty == "isPrime" )
+
+ if ( loadoutType == "pilot" )
+ {
+ player.p.pilotLoadoutChanged = true
+
+ if ( updateModel )
+ player.p.pilotModelNeedsUpdate = loadoutIndex
+ }
+ else
+ {
+ player.p.titanLoadoutChanged = true
+
+ if ( updateModel )
+ player.p.titanModelNeedsUpdate = loadoutIndex
+ }
+
+ thread UpdateCachedLoadouts()
+}
+
+string function GetRefFromLoadoutTypeIndexPropertyAndValue( entity player, string loadoutType, int index, string property, var value )
+{
+ bool isPilotLoadout = ( loadoutType == "pilot" )
+ bool isTitanLoadout = ( loadoutType == "titan" )
+ if ( !isPilotLoadout && !isTitanLoadout )
+ return INVALID_REF
+
+ switch ( property )
+ {
+ case "isPrime":
+ {
+ if ( !isTitanLoadout )
+ return INVALID_REF
+
+ if( PersistenceEnumValueIsValid( "titanIsPrimeTitan", value ) )
+ return GetTitanRefFromPersistenceLoadoutIndexAndValueForIsPrime( index, expect string( value ) )
+ else
+ return INVALID_REF
+ }
+
+ case "camoIndex": //Camos work across all Titans, all pilots, and all weapons.
+ case "primeCamoIndex":
+ {
+ int valueAsInt = ForceCastVarToInt( value )
+ if ( !IsValidCamoIndex( valueAsInt ) ) //Note that we should have already checked for the case that we're setting a Titan's camo to -1 ( valid warpaint use case ) in FailsLoadoutValidationCheck()
+ {
+ return INVALID_REF
+ }
+ else
+ {
+ if ( isPilotLoadout )
+ return CamoSkins_GetByIndex( valueAsInt ).pilotCamoRef
+ else
+ return CamoSkins_GetByIndex( valueAsInt ).titanCamoRef
+ }
+ }
+ break
+
+ case "primaryCamoIndex":
+ case "secondaryCamoIndex":
+ case "weapon3CamoIndex":
+ {
+ int valueAsInt = ForceCastVarToInt( value )
+ if ( !IsValidCamoIndex( valueAsInt ) )
+ return INVALID_REF
+ else
+ return CamoSkins_GetByIndex( valueAsInt ).ref
+ }
+ break
+
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex":
+ {
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string weaponRef = GetPersistentLoadoutValue( player, loadoutType, index, parentProperty )
+ int valueAsInt = ForceCastVarToInt( value )
+ return GetSkinRefFromWeaponRefAndPersistenceValue( weaponRef, valueAsInt )
+ }
+ break
+
+ case "decalIndex":
+ case "primeDecalIndex": //Assume same nose arts work for both prime and non-prime titans
+ {
+ if ( !isTitanLoadout )
+ return INVALID_REF
+
+ string titanClass = GetDefaultTitanLoadout( index ).titanClass
+ int valueAsInt = ForceCastVarToInt( value )
+ return GetNoseArtRefFromTitanClassAndPersistenceValue( titanClass, valueAsInt )
+ }
+ break
+
+ case "primeSkinIndex": //Primes don't have war paints, white listed skins for titans (0 and 2) should have already been handled earlier
+ {
+ return INVALID_REF
+ }
+ break
+
+ case "skinIndex":
+ {
+ if ( !isTitanLoadout ) //Only titans have warpaints. White listed skins for pilots should already have been handled earlier
+ return INVALID_REF
+
+ string titanClass = GetDefaultTitanLoadout( index ).titanClass
+ int valueAsInt = ForceCastVarToInt( value )
+ return GetSkinRefFromTitanClassAndPersistenceValue( titanClass, valueAsInt )
+ }
+ break
+
+ default:
+ return string( value )
+ }
+
+ unreachable
+}
+
+bool function IsValidCamoIndex( int camoIndex )
+{
+ int numCamos = CamoSkins_GetCount() //Note that we should have already handled the case for a titan's camoIndex to be -1 (valid warpaint usecase) in FailsLoadoutValidationCheck
+ return ((camoIndex >= 0) && (camoIndex < numCamos))
+}
+
+string function GetTitanRefFromPersistenceLoadoutIndexAndValueForIsPrime( int index, string value )
+{
+ bool isPrimeRef = (value == "titan_is_prime")
+ string titanClass = GetDefaultTitanLoadout( index ).titanClass
+ if ( isPrimeRef )
+ return GetPrimeTitanRefForTitanClass( titanClass )
+
+ return titanClass
+}
+
+bool function LoadoutPropertyRequiresItemValidation( string loadoutProperty )
+{
+ bool shouldSkipValidation = ( GetCurrentPlaylistVarInt( "skip_loadout_validation", 0 ) == 1 )
+ if ( shouldSkipValidation )
+ {
+ //printt( "skip_loadout_validation" )
+ return false
+ }
+
+ if ( loadoutProperty == "name" )
+ return false
+
+ if ( loadoutProperty == "showArmBadge" )
+ return false
+
+ if ( loadoutProperty == "primarySkinIndex" || loadoutProperty == "secondarySkinIndex" || loadoutProperty == "weapon3SkinIndex")
+ return false
+
+ return true
+}
+
+// Only checks primary mods and attachments are valid in itemData and the parent isn't locked
+bool function IsLoadoutSubitemValid( entity player, string loadoutType, int loadoutIndex, string property, string ref )
+{
+ string childRef = ""
+
+ switch ( property )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ if ( loadoutType == "pilot" )
+ {
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, parentProperty )
+ if ( ref != "" )
+ childRef = ref
+ ref = expect string( player.GetPersistentVar( loadoutString ) )
+ }
+ break
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ return true
+ break
+ }
+ // TODO: Seems bad to pass null childRef on to some of the checks below if the property wasn't one of the above
+
+ // invalid attachment
+ if ( childRef != "" && !HasSubitem( ref, childRef ) )
+ return false
+
+ //if ( IsItemLocked( player, childRef, expect string( ref ) ) )
+ // return false
+
+ return true
+}
+
+void function SetPlayerPersistentVarWithoutValidation( entity player, string loadoutType, int loadoutIndex, string propertyName, string value )
+{
+ string persistentLoadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, propertyName )
+
+ if ( GetPersistentLoadoutPropertyType( propertyName ) == "int" )
+ player.SetPersistentVar( persistentLoadoutString, int( value ) )
+ else
+ player.SetPersistentVar( persistentLoadoutString, value )
+}
+
+string function ResetLoadoutPropertyToDefault( entity player, string loadoutType, int loadoutIndex, string propertyName )
+{
+ string defaultValue = GetLoadoutPropertyDefault( loadoutType, loadoutIndex, propertyName )
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, propertyName, defaultValue )
+ return defaultValue
+}
+
+bool function FailsLoadoutValidationCheck( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, var value )
+{
+ //printt( "FailsLoadoutValidationCheck, player: " + player + ", loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", loadoutProperty: " + loadoutProperty + ", value: " + value )
+
+ int initializedVersion = player.GetPersistentVarAsInt( "initializedVersion" )
+ bool previouslyInitialized = ( initializedVersion >= PERSISTENCE_INIT_VERSION )
+ if ( !previouslyInitialized ) //Special case: if we're intializing from defaults/updating persistent data, don't try to reset to defaults
+ {
+ //printt( "!previouslyInitialized" )
+ return false
+ }
+
+ if ( AllowCamoIndexToSkipValidation( loadoutType, loadoutProperty, value ) ) //Special case: Warpaints will set camoIndex
+ {
+// printt( "AllowCamoIndexToSkipValidation" )
+ return false
+ }
+
+ if ( AllowSkinIndexToSkipValidation( loadoutType, loadoutProperty, value ) ) //Skins need to be handled differently because they are only valid refs for warpaints, and we set values of 0, 1 and 2 for different cases
+ {
+// printt( "AllowSkinIndexToSkipValidation" )
+ return false
+ }
+
+ string ref = GetRefFromLoadoutTypeIndexPropertyAndValue( player, loadoutType, loadoutIndex, loadoutProperty, value )
+ if ( !IsRefValid( ref ) )
+ {
+ printt( "!IsRefValid( " + ref + " ), generated from loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ". loadoutProperty: " + loadoutProperty + ", value: " + value )
+ return true
+ }
+
+ if ( IsSettingPrimeTitanWithoutSetFile( player, loadoutType, loadoutProperty, loadoutIndex, value ) ) //Hack, working around fact that we allow setting Prime Titan value since it's entitlement based unlock.
+ {
+ //printt( "IsSettingPrimeTitanWithoutSetFile, player: " + player + ", ref: " + ref )
+ return true
+ }
+
+ if ( FailsItemLockedValidationCheck( player, loadoutType, loadoutIndex, loadoutProperty, ref ) )
+ {
+ printt( "FailsItemLockedValidationCheck, player: " + player + ", ref: " + ref )
+ return true
+ }
+
+ if ( !IsValueValidForLoadoutTypeIndexAndProperty( loadoutType, loadoutIndex, loadoutProperty, value, ref ) )
+ {
+ //printt( "!IsValueValidForLoadoutTypeIndexAndProperty, loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", loadoutProperty: " + loadoutProperty + ", value: " + value + " ref: " + ref )
+ return true
+ }
+
+ if ( ( loadoutProperty == "secondary" || loadoutProperty == "weapon3" ) && !IsValueValidForPropertyWithCategoryRestriction( player, loadoutIndex, loadoutProperty, string( value ) ) )
+ return true
+
+ //printt( "End FailsLoadoutValidationCheck" )
+ return false
+}
+
+bool function IsValueValidForPropertyWithCategoryRestriction( entity player, int loadoutIndex, string targetProperty, string targetValue )
+{
+ Assert( targetProperty == "secondary" || targetProperty == "weapon3" )
+
+ string otherProperty
+ if ( targetProperty == "secondary" )
+ otherProperty = "weapon3"
+ else
+ otherProperty = "secondary"
+
+ string otherValue = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, otherProperty )
+
+ if ( ItemsInSameMenuCategory( targetValue, otherValue ) )
+ return false
+
+ return true
+}
+
+bool function FailsItemLockedValidationCheck( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string ref )
+{
+ bool shouldSkipValidation = ( GetCurrentPlaylistVarInt( "skip_loadout_item_locked_validation", 0 ) == 1 )
+ if ( shouldSkipValidation )
+ {
+ //printt( "skip_loadout_item_locked_validation" )
+ return false
+ }
+
+ if ( LoadoutIsLocked( player, loadoutType, loadoutIndex ) )
+ {
+ //printt( "loadout of type " + loadoutType + " and index " + loadoutIndex + " is locked, skip checking if items under it are locked" )
+ return false
+ }
+
+ if ( IsSubItemType( GetItemType( ref ) ) )
+ {
+ string parentRef = GetParentRefFromLoadoutInfoAndRef( player, loadoutType, loadoutIndex, loadoutProperty, ref )
+ if ( parentRef == "" )
+ {
+ //printt( "parentRef == blankstring" )
+ return true
+ }
+
+ if ( !SkipItemLockedCheck( player, ref, parentRef, loadoutProperty ) )
+ {
+ if ( IsSubItemLocked( player, ref, parentRef ) )
+ {
+ //printt( "IsSubItemLocked, player: " + player + ", ref: " + ref + ", parentRef: " + parentRef )
+ return true
+ }
+ }
+ }
+ else
+ {
+ if ( !SkipItemLockedCheck( player, ref, "", loadoutProperty ) )
+ {
+ if ( IsItemLocked( player, ref ) ) //Somehow we are trying to get something that is locked. Might be rogue client sending bad values?
+ {
+ //printt( "IsItemLocked, player: " + player + ", ref: " + ref )
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+bool function IsSettingPrimeTitanWithoutSetFile( entity player, string loadoutType, string loadoutProperty, int loadoutIndex, var value ) //Temp function, remove once all titans have prime Titans
+{
+ if ( loadoutType != "titan" )
+ return false
+
+ if ( loadoutProperty != "isPrime" )
+ return false
+
+ if ( value != "titan_is_prime" )
+ return false
+
+ string titanClass = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass" )
+
+ return ( !TitanClassHasPrimeTitan( titanClass ) )
+}
+
+bool function SkipItemLockedCheck( entity player, string ref, string parentRef, string loadoutProperty ) //Hack: Skip entitlement related unlock checks for now. Can fail.
+{
+ if ( DevEverythingUnlocked() )
+ return true
+
+ //if ( IsItemInEntitlementUnlock( ref ) && IsLobby() ) //TODO: Look into restricting this to lobby only? But entitlement checks can fail randomly...
+ if ( IsItemInEntitlementUnlock( ref, parentRef ) )
+ return true
+
+ if ( loadoutProperty == "isPrime" ) //Temp check to avoid weirdness where we can set titan_is_prime but not titan_is_not_prime because prime titan refs are not child refs of non prime titan refs. Should be able to remove this once all prime titans are in game.
+ return true
+
+ return false
+}
+
+bool function AllowCamoIndexToSkipValidation( string loadoutType, string loadoutProperty, var value ) //HACK: working around the fact that we do SetCamo( -1 ) when applying warpaints
+{
+ if ( loadoutProperty != "camoIndex" ) //Prime Titans currently don't have warpaints, so not supporting setting primeCamoIndex to be -1
+ return false
+
+ int valueAsInt = ForceCastVarToInt( value )
+ if ( valueAsInt == CAMO_INDEX_BASE )
+ return true
+
+ if ( loadoutType != "titan" )
+ return false
+
+ if ( valueAsInt != TITAN_WARPAINT_CAMO_INDEX )
+ return false
+
+ return true
+}
+
+bool function AllowSkinIndexToSkipValidation( string loadoutType, string loadoutProperty, var value ) //TODO: test weapon skin/camo combinations?
+{
+ int valueAsInt = ForceCastVarToInt( value ) //TODO: Awkward, getting around the fact that you can't do int( value ) if it's already an int.
+
+ switch( loadoutProperty )
+ {
+ case "skinIndex":
+ {
+ if ( loadoutType == "pilot" )
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == PILOT_SKIN_INDEX_CAMO) )
+ else
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == TITAN_SKIN_INDEX_CAMO) ) //War paints will have different values, hence we must validate them
+ }
+ break
+
+ case "primeSkinIndex": //Prime Titans don't support war paints, so should only support values of 0 and 2
+ {
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == TITAN_SKIN_INDEX_CAMO) )
+ }
+ break
+
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex":
+ {
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == WEAPON_SKIN_INDEX_CAMO) )
+ }
+ break
+
+ default:
+ return false
+ }
+
+ unreachable
+}
+
+bool function IsValueValidForLoadoutTypeIndexAndProperty( string loadoutType, int loadoutIndex, string loadoutProperty, var value, string ref )
+{
+ bool isTitanLoadout = (loadoutType == "titan")
+ bool isPilotLoadout = (loadoutType == "pilot")
+ if ( !isTitanLoadout && !isPilotLoadout )
+ return false
+
+ //Should have run IsRefValid( ref already, so should be fine just doing GetItemType( ref ) )
+ int itemType = GetItemType( ref )
+
+ //printt( "itemType : " + itemType )
+ switch ( loadoutProperty )
+ {
+ case "suit":
+ return ( (itemType == eItemTypes.PILOT_SUIT) && isPilotLoadout )
+
+ case "race":
+ return ( (itemType == eItemTypes.RACE) && isPilotLoadout )
+
+ case "execution":
+ return ( (itemType == eItemTypes.PILOT_EXECUTION) && isPilotLoadout )
+
+ case "titanExecution":
+ return ( isTitanLoadout && IsValidTitanExecution( loadoutIndex, loadoutProperty, value, ref ) )
+
+ case "primary": //Only Pilots store their primary in persistence
+ return ( (itemType == eItemTypes.PILOT_PRIMARY) && isPilotLoadout )
+
+ case "primaryAttachment": //Only Pilots their primary in persistence
+ return ( (itemType == eItemTypes.SUB_PILOT_WEAPON_ATTACHMENT || itemType == eItemTypes.PILOT_PRIMARY_ATTACHMENT) && isPilotLoadout )
+
+ case "primaryMod1": //Only Pilots have primary mods in persistence
+ case "primaryMod2":
+ case "primaryMod3":
+ return ( (itemType == eItemTypes.SUB_PILOT_WEAPON_MOD || itemType == eItemTypes.PILOT_PRIMARY_MOD) && isPilotLoadout )
+
+ case "secondary": //Only Pilots store their secondary in persistence
+ case "weapon3":
+ return ( (itemType == eItemTypes.PILOT_SECONDARY) && isPilotLoadout )
+
+ case "secondaryMod1": //Only Pilots store their secondary in persistence
+ case "secondaryMod2": //Only Pilots store their secondary in persistence
+ case "secondaryMod3": //Only Pilots store their secondary in persistence
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ return ( (itemType == eItemTypes.SUB_PILOT_WEAPON_MOD || itemType == eItemTypes.PILOT_SECONDARY_MOD) && isPilotLoadout )
+
+ case "ordnance":
+ return ( (itemType == eItemTypes.PILOT_ORDNANCE) && isPilotLoadout )
+
+ case "passive1":
+ if ( isPilotLoadout )
+ {
+ return ( itemType == eItemTypes.PILOT_PASSIVE1 )
+ }
+ else
+ {
+ return IsValidTitanPassive( loadoutIndex, loadoutProperty, value, ref )
+ }
+
+ case "passive2":
+ if ( isPilotLoadout )
+ return ( itemType == eItemTypes.PILOT_PASSIVE2 )
+ else
+ return IsValidTitanPassive( loadoutIndex, loadoutProperty, value, ref )
+
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ return ( isTitanLoadout && IsValidTitanPassive( loadoutIndex, loadoutProperty, value, ref ) )
+
+ case "isPrime":
+ return ( isTitanLoadout && PersistenceEnumValueIsValid( "titanIsPrimeTitan", value ) ) //Technically will be covered by enumValue checks before LoadoutValidation checks, but included here for completeness
+
+ //TODO: Need to get ref for these correctly!
+ case "skinIndex":
+ case "camoIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "decalIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ return true // assume already validated
+
+ case "titanClass": //Should never be able to set these things normally!
+ case "primaryMod":
+ case "special":
+ case "antirodeo":
+ string defaultValue = GetLoadoutPropertyDefault( loadoutType, loadoutIndex, loadoutProperty )
+ return ( value == defaultValue )
+
+ default:
+ Assert( false, "Unknown loadoutProperty " + loadoutProperty )
+ return false
+ }
+
+ unreachable
+}
+
+bool function ValidateSkinAndCamoIndexesAsAPair( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, var value )
+{
+ bool isSkinIndex = false
+ switch( loadoutProperty )
+ {
+ case "skinIndex":
+ case "primeSkinIndex":
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex":
+ isSkinIndex = true
+ break
+ }
+
+ if ( !isSkinIndex )
+ return false
+
+ int valueAsInt = ForceCastVarToInt( value )
+
+ string camoProperty = GetCorrectCamoProperty( loadoutProperty )
+ int camoIndexValue = GetValidatedPersistentLoadoutValueInt( player, loadoutType, loadoutIndex, camoProperty )
+
+
+ //printt( "ValidateSkinAndCamoIndexesAsAPair, player: " + player + " loadouType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", skinProperty: " + loadoutProperty + ", skin Value: " + string( value ) + ", camoProperty: " + camoProperty + ", camo Value: " + camoIndexValue )
+
+ if ( valueAsInt == SKIN_INDEX_BASE ) //Just set to default of 0, 0.
+ return SetCamoIndexToValue( player, loadoutType, loadoutIndex, camoProperty, CAMO_INDEX_BASE )
+
+ switch ( loadoutProperty )
+ {
+ case "skinIndex":
+ {
+ if (loadoutType == "pilot" ) //1 is skin for camo, only other valid value is 0
+ {
+ if ( valueAsInt == PILOT_SKIN_INDEX_CAMO && camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return true
+ }
+ }
+ else if ( loadoutType == "titan" ) //2 is skin for camos, all other skin values other than 0 are warpaints that have camo index == -1
+ {
+ if ( valueAsInt == TITAN_SKIN_INDEX_CAMO ) //This is a camo skin
+ {
+ if ( camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return true
+ }
+ }
+ else //i.e. this is a warpaint
+ {
+ return SetCamoIndexToValue( player, loadoutType, loadoutIndex, camoProperty, TITAN_WARPAINT_CAMO_INDEX ) //Set to default of -1 for warpaint
+ }
+ }
+
+ break
+ }
+
+ case "primeSkinIndex": //switching between prime and non-prime doesn't have problems where skin is unlocked on non-prime but is locked on prime, so no need to return false
+ {
+ if ( valueAsInt == TITAN_SKIN_INDEX_CAMO && camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return true
+ }
+
+ break
+ }
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex": //1 is skin for camo
+ {
+ if ( valueAsInt == WEAPON_SKIN_INDEX_CAMO )
+ {
+ if ( camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return false // HACK: Work around fact that when we switch weapons we don't do a corresponding call to set camo and skin indexes
+ }
+ }
+ else
+ {
+ return SetCamoIndexToValue( player, loadoutType, loadoutIndex, camoProperty, 0 ) //Set to default of 0 for weapon skins
+ }
+
+ break
+ }
+ }
+
+ return false
+}
+
+bool function SetCamoIndexToValue( entity player, string loadoutType, int loadoutIndex, string camoProperty, int setValue ) //HACK HACK: should fix dependency between skin and camo index on UI side. Remove when done.
+{
+ int camoIndexValue = GetPersistentLoadoutValueInt( player, loadoutType, loadoutIndex, camoProperty )
+
+ if ( camoIndexValue == setValue )
+ return false
+
+ //printt( "SetCamoIndexToValue, player: " + player + " loadouType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", camoProperty: " + camoProperty + ", setValue: " + string( setValue ))
+
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, camoProperty, string( setValue ) )
+
+ return true
+}
+
+void function ResetSkinAndCamoIndexesToZero( entity player, string loadoutType, int loadoutIndex, string skinProperty, string camoProperty )
+{
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, camoProperty, "0" )
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, skinProperty, "0" )
+}
+
+string function GetCorrectCamoProperty( string loadoutProperty ) //HACK HACK: should fix dependency between skin and camo index on UI side. Remove when done.
+{
+ string returnValue
+ switch( loadoutProperty )
+ {
+ case "skinIndex":
+ returnValue = "camoIndex"
+ break
+
+ case "primeSkinIndex":
+ returnValue = "primeCamoIndex"
+ break
+
+ case "primarySkinIndex":
+ returnValue = "primaryCamoIndex"
+ break
+
+ case "secondarySkinIndex":
+ returnValue = "secondaryCamoIndex"
+ break
+
+ case "weapon3SkinIndex":
+ returnValue = "weapon3CamoIndex"
+ break
+
+ default:
+ unreachable
+ }
+
+ return returnValue
+}
+
+bool function IsValidTitanPassive( int loadoutIndex, string loadoutProperty, var value, string ref ) //TODO: Not using all parameters in this function yet, might need them for full validation
+{
+ //Should have run IsRefValid(ref already, so fine to do GetItemType( ref ))
+ int itemType = GetItemType( ref )
+ //printt( "itemType: " + itemType )
+
+ switch ( loadoutProperty )
+ {
+ case "passive1":
+ return (itemType == eItemTypes.TITAN_GENERAL_PASSIVE)
+
+ case "passive2":
+ {
+ switch( loadoutIndex ) //TODO: Hard coded, not great!
+ {
+ case 0:
+ return itemType == eItemTypes.TITAN_ION_PASSIVE
+
+ case 1:
+ return itemType == eItemTypes.TITAN_SCORCH_PASSIVE
+
+ case 2:
+ return itemType == eItemTypes.TITAN_NORTHSTAR_PASSIVE
+
+ case 3:
+ return itemType == eItemTypes.TITAN_RONIN_PASSIVE
+
+ case 4:
+ return itemType == eItemTypes.TITAN_TONE_PASSIVE
+
+ case 5:
+ return itemType == eItemTypes.TITAN_LEGION_PASSIVE
+
+ case 6:
+ return itemType == eItemTypes.TITAN_VANGUARD_PASSIVE
+
+ default:
+ return false
+ }
+ }
+
+ case "passive3":
+ return (itemType == eItemTypes.TITAN_TITANFALL_PASSIVE)
+
+ case "passive4":
+ return (itemType == eItemTypes.TITAN_UPGRADE1_PASSIVE)
+ case "passive5":
+ return (itemType == eItemTypes.TITAN_UPGRADE2_PASSIVE)
+ case "passive6":
+ return (itemType == eItemTypes.TITAN_UPGRADE3_PASSIVE)
+
+ default:
+ unreachable
+ }
+
+ unreachable
+}
+
+bool function IsValidTitanExecution( int loadoutIndex, string loadoutProperty, var value, string ref ) //TODO: Not using all parameters in this function yet, might need them for full validation
+{
+ //Should have run IsRefValid(ref already, so fine to do GetItemType( ref ))
+ int itemType = GetItemType( ref )
+ //printt( "itemType: " + itemType )
+
+ switch ( loadoutProperty )
+ {
+ case "titanExecution":
+ {
+ switch( loadoutIndex ) //TODO: Hard coded, not great!
+ {
+ case 0:
+ return itemType == eItemTypes.TITAN_ION_EXECUTION
+
+ case 1:
+ return itemType == eItemTypes.TITAN_SCORCH_EXECUTION
+
+ case 2:
+ return itemType == eItemTypes.TITAN_NORTHSTAR_EXECUTION
+
+ case 3:
+ return itemType == eItemTypes.TITAN_RONIN_EXECUTION
+
+ case 4:
+ return itemType == eItemTypes.TITAN_TONE_EXECUTION
+
+ case 5:
+ return itemType == eItemTypes.TITAN_LEGION_EXECUTION
+
+ case 6:
+ return itemType == eItemTypes.TITAN_VANGUARD_EXECUTION
+
+ default:
+ return false
+ }
+ }
+
+ default:
+ unreachable
+ }
+
+ unreachable
+}
+
+string function GetParentRefFromLoadoutInfoAndRef( entity player, string loadoutType, int loadoutIndex, string property, string childRef )
+{
+ bool isPilotLoadout = (loadoutType == "pilot")
+ bool isTitanLoadout = (loadoutType == "titan")
+ if ( !isPilotLoadout && !isTitanLoadout )
+ return ""
+
+ switch ( property )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ {
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, parentProperty )
+ string resultString = string( player.GetPersistentVar( loadoutString ) ) //titanLoadouts[5].titanClass
+ if ( HasSubitem( resultString, childRef ) )
+ return resultString
+ else
+ return ""
+ }
+
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ {
+ if ( isTitanLoadout && !IsValidTitanPassive( loadoutIndex, property, childRef, childRef ) )
+ return ""
+
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, parentProperty )
+ string resultString = string( player.GetPersistentVar( loadoutString ) ) //titanLoadouts[5].titanClass
+ if ( HasSubitem( resultString, childRef ) )
+ return resultString
+ else
+ return ""
+ }
+
+ case "skinIndex": //These should all only be allowed to be changed on Titans
+ case "decalIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ case "camoIndex": //For pilots, this is not a subitem. For titans, it should be titanclass.
+ {
+ if ( !isTitanLoadout )
+ return "" //Only Titans have war paints, which allow for arbitrary skin index. All others should have been taken care of earlier in AllowSkinIndexToSkipValidation()
+
+ return GetDefaultTitanLoadout( loadoutIndex ).titanClass
+ }
+
+ case "primaryCamoIndex": //For Pilots, subitem is primary. For Titans, subitem is titanclass
+ {
+ if ( isPilotLoadout )
+ {
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "primary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+ else
+ {
+ return GetDefaultTitanLoadout( loadoutIndex ).titanClass
+ }
+ }
+
+ case "secondaryCamoIndex":
+ {
+ if ( !isPilotLoadout ) //Only Pilots have secondaries
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "secondary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "weapon3CamoIndex":
+ {
+ if ( !isPilotLoadout )
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "weapon3" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "primarySkinIndex":
+ {
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "primary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "secondarySkinIndex":
+ {
+ if ( !isPilotLoadout ) //Only Pilots have secondaries
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "secondary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "weapon3SkinIndex":
+ {
+ if ( !isPilotLoadout )
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "weapon3" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "primeSkinIndex": //Should be taken care of earlier in AllowSkinIndexToSkipValidation()
+ default:
+ return ""
+ }
+
+ unreachable
+}
+
+int function ForceCastVarToInt( var value ) //HACK: working around inability to cast an int to int. Reexamine when less busy!
+{
+ if ( typeof( value ) == "int" )
+ return ( expect int( value ) )
+
+ return int( value )
+}
+#endif //Server only
+
+string function GetLoadoutPropertyDefault( string loadoutType, int loadoutIndex, string propertyName ) //Bad things go wrong if you give the wrong default!
+{
+ string resultString
+ switch ( propertyName )
+ {
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ resultString = "0"
+ break
+
+ case "isPrime":
+ resultString = "titan_is_not_prime"
+ break
+
+ case "execution":
+ resultString = "execution_neck_snap" //HACK, not sure where this is getting set otherwise.
+ break
+
+ case "titanExecution":
+ resultString = "execution_random_" + loadoutIndex //Loadout Index for Titans is synced to a specific Titan.
+ break
+
+ default:
+ {
+ bool isTitanLoadout = (loadoutType == "titan")
+ bool isPilotLoadout = (loadoutType == "pilot")
+ if ( isPilotLoadout )
+ {
+ PilotLoadoutDef defaultPilotLoadout = GetDefaultPilotLoadout( loadoutIndex ) //HACK: note that this can give an invalid default for a child property, e.g. a sight that doesn't exist on this weapon. This is handled later in ResolveInvalidLoadoutChildValues
+ resultString = GetPilotLoadoutValue( defaultPilotLoadout, propertyName )
+ }
+ else if ( isTitanLoadout )
+ {
+ TitanLoadoutDef defaultPilotLoadout = GetDefaultTitanLoadout( loadoutIndex ) //HACK: note that this can give an invalid default for a child property, e.g. a sight that doesn't exist on this weapon. This is handled later in ResolveInvalidLoadoutChildValues
+ resultString = GetTitanLoadoutValue( defaultPilotLoadout, propertyName )
+ }
+ else
+ {
+ unreachable
+ }
+ break
+ }
+ }
+
+ //printt( "GetLoadoutPropertyDefault, loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", propertyName: " + propertyName + ", resultString: " + resultString )
+ return resultString
+}
+
+string function GetCategoryRestrictedResetValue( entity player, int loadoutIndex, string targetProperty, string otherProperty )
+{
+ string loadoutType = "pilot"
+ string otherPropertyValue = GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, otherProperty )
+ string potentialDefaultValue = GetLoadoutPropertyDefault( loadoutType, loadoutIndex, targetProperty )
+ string resetValue = potentialDefaultValue
+
+ if ( otherPropertyValue != "" )
+ {
+ int otherCategory = expect int( GetItemDisplayData( otherPropertyValue ).i.menuCategory )
+ int potentialDefaultCategory = expect int( GetItemDisplayData( potentialDefaultValue ).i.menuCategory )
+
+ if ( potentialDefaultCategory == otherCategory )
+ {
+ Assert( otherCategory == eSecondaryWeaponCategory.AT || otherCategory == eSecondaryWeaponCategory.PISTOL )
+ if ( otherCategory == eSecondaryWeaponCategory.AT )
+ resetValue = GetWeaponCategoryBasedDefault( eSecondaryWeaponCategory.PISTOL )
+ else
+ resetValue = GetWeaponCategoryBasedDefault( eSecondaryWeaponCategory.AT )
+ }
+ }
+
+ return resetValue
+}
+
+string function GetWeaponCategoryBasedDefault( int category )
+{
+ string val
+
+ switch ( category )
+ {
+ case eSecondaryWeaponCategory.AT:
+ val = "mp_weapon_defender"
+ break
+
+ case eSecondaryWeaponCategory.PISTOL:
+ val = "mp_weapon_autopistol"
+ break
+
+ default:
+ Assert( 0, "category: " + DEV_GetEnumStringFromIndex( "eSecondaryWeaponCategory", category ) + " not handled in GetWeaponCategoryBasedDefault()" )
+ break
+ }
+
+ return val
+}
+
+string function BuildPersistentVarAccessorString( string loadoutType, int loadoutIndex, string propertyName )
+{
+ return loadoutType + "Loadouts[" + loadoutIndex + "]." + propertyName
+}
+
+bool function LoadoutIsLocked( entity player, string loadoutType, int loadoutIndex )
+{
+ if ( loadoutType == "titan" )
+ {
+ string titanClass = GetLoadoutPropertyDefault( "titan", loadoutIndex, "titanClass" )
+ return IsItemLocked( player, titanClass )
+
+ }
+ else if ( loadoutType == "pilot" )
+ {
+ string pilotLoadoutRef = "pilot_loadout_" + ( loadoutIndex + 1 )
+ return IsItemLocked( player, pilotLoadoutRef )
+ }
+
+ unreachable
+
+}
+
+string function GetValidatedPersistentLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ #if SERVER
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+ Assert( loadoutIndex < PersistenceGetArrayCount( loadoutType + "Loadouts" ), "Invalid loadoutIndex: " + loadoutIndex )
+
+ var loadoutPropertyEnum = null
+ if ( loadoutType == "pilot" )
+ {
+ bool isValidPilotLoadoutProperty = IsValidPilotLoadoutProperty( loadoutProperty )
+ //Assert( isValidPilotLoadoutProperty, "Invalid pilot loadoutProperty: " + loadoutProperty )
+ if( !isValidPilotLoadoutProperty )
+ {
+ CodeWarning( "Invalid pilot loadoutProperty: " + loadoutProperty )
+ return ""
+ }
+
+ loadoutPropertyEnum = GetPilotLoadoutPropertyEnum( loadoutProperty )
+ }
+ else
+ {
+ bool isValidTitanLoadoutProperty = IsValidTitanLoadoutProperty( loadoutProperty )
+ //Assert( isValidTitanLoadoutProperty, "Invalid titan loadoutProperty: " + loadoutProperty )
+ if ( !isValidTitanLoadoutProperty )
+ {
+ CodeWarning( "Invalid titan loadoutProperty: " + loadoutProperty )
+ return ""
+ }
+ loadoutPropertyEnum = GetTitanLoadoutPropertyEnum( loadoutProperty )
+ }
+
+ string getString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, loadoutProperty )
+ var value = player.GetPersistentVar( getString )
+
+ if ( value == null )
+ value = ""
+
+ if ( loadoutPropertyEnum != null )
+ {
+ if ( !PersistenceEnumValueIsValid( loadoutPropertyEnum, value ) )
+ {
+ printt( "Invalid Loadout Property: ", loadoutType, loadoutIndex, loadoutProperty, value )
+ value = ResetLoadoutPropertyToDefault( player, loadoutType, loadoutIndex, loadoutProperty ) //TODO: This will call player.SetPersistentVar() directly. Awkward to do this in a getter function
+ ClientCommand( player, "disconnect #RESETTING_LOADOUT", 0 ) //Kick player out with a "Resetting Invalid Loadout" message. Mainly necessary so UI/Client script don't crash out later with known, bad data from persistence
+ }
+ }
+
+ if ( LoadoutPropertyRequiresItemValidation( loadoutProperty ) && value != "" )
+ {
+ if ( FailsLoadoutValidationCheck( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ printt( "Invalid Loadout Property: ", loadoutType, loadoutIndex, loadoutProperty, value )
+ value = ResetLoadoutPropertyToDefault( player, loadoutType, loadoutIndex, loadoutProperty ) //TODO: This will call player.SetPersistentVar() directly. Awkward to do this in a getter function
+ ClientCommand( player, "disconnect #RESETTING_LOADOUT", 0 ) //Kick player out with a "Resetting Invalid Loadout" message. Mainly necessary so UI/Client script don't crash out later with known, bad data from persistence
+ }
+
+ ValidateSkinAndCamoIndexesAsAPair( player, loadoutType, loadoutIndex, loadoutProperty, value ) //TODO: This is awkward, has the potential to call a SetPersistentLoadoutValue() if skinIndex and camoIndex are not correct as a pair
+ }
+
+ return string( value )
+ #else
+ return GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, loadoutProperty )
+ #endif
+}
+
+int function GetValidatedPersistentLoadoutValueInt( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ #if SERVER
+ string returnValue = GetValidatedPersistentLoadoutValue( player, loadoutType, loadoutIndex, loadoutProperty )
+ return int( returnValue )
+ #else
+ return GetPersistentLoadoutValueInt( player, loadoutType, loadoutIndex, loadoutProperty )
+ #endif
+}
+
+
+// SERVER: Looks at and updates persistent loadout data
+// CLIENT/UI: Looks at and updates cached loadout data
+void function ResolveInvalidLoadoutChildValues( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string parentValue )
+{
+ array<string> childProperties = GetChildLoadoutProperties( loadoutType, loadoutProperty )
+
+ //if ( childProperties.len() )
+ //{
+ // printt( "====== loadoutProperty:", loadoutProperty, "childProperties are:" )
+ // foreach ( childProperty in childProperties )
+ // printt( "====== ", childProperty )
+ //}
+
+ if ( childProperties.len() )
+ {
+ Assert( parentValue != "" )
+
+ foreach ( childProperty in childProperties )
+ {
+ string childValue
+
+ if ( childProperty == "primaryCamoIndex" || childProperty == "secondaryCamoIndex" || childProperty == "weapon3CamoIndex" ) // TODO: Keep skin on weapon change if possible
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will blind RESET" )
+ }
+ else if ( childProperty == "primarySkinIndex" || childProperty == "secondarySkinIndex" || childProperty == "weapon3SkinIndex" )
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will blind RESET" )
+ }
+ else if ( (childProperty == "primaryMod2" || childProperty == "primaryMod3") && IsSubItemLocked( player, childProperty.tolower(), parentValue ) ) // check primarymod2 or primarymod3 feature locked
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET due to feature lock" )
+ }
+ else if ( (childProperty == "secondaryMod2" || childProperty == "weapon3Mod2") && IsSubItemLocked( player, "secondarymod2", parentValue ) ) // check secondarymod2 feature locked
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET due to feature lock" )
+ }
+ else if ( (childProperty == "secondaryMod3" || childProperty == "weapon3Mod3") && IsSubItemLocked( player, "secondarymod3", parentValue ) ) // check secondarymod3 feature locked
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET due to feature lock" )
+ }
+ else // check subitem locked
+ {
+ #if SERVER
+ // TODO: This call will actually check and change the persistent data if it's invalid. Doesn't break anything here, but this function really shouldn't change persistent data.
+ childValue = GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, childProperty )
+ #else
+ if ( loadoutType == "pilot" )
+ childValue = GetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], childProperty )
+ else if ( loadoutType == "titan" )
+ childValue = GetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], childProperty )
+ else
+ CodeWarning( "Unhandled loadoutType: " + loadoutType )
+ #endif
+
+ if ( childValue == "" || ( childValue != "" && HasSubitem( parentValue, childValue ) && !IsSubItemLocked( player, childValue, parentValue ) ) )
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will KEEP childValue: \"" + childValue + "\"" )
+ continue
+ }
+ //else
+ //{
+ // printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET childValue: \"" + childValue + "\"" )
+ //}
+ }
+
+ childValue = GetLoadoutChildPropertyDefault( loadoutType, childProperty, parentValue )
+
+ #if SERVER
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, childProperty, childValue )
+ #else
+ if ( loadoutType == "pilot" )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], childProperty, childValue )
+ else if ( loadoutType == "titan" )
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], childProperty, childValue )
+ else
+ CodeWarning( "Unhandled loadoutType: " + loadoutType )
+ #endif
+ }
+ }
+ //else
+ //{
+ // printt( "====== loadoutProperty:", loadoutProperty, "childProperties is empty" )
+ //}
+}
+
+#if UI || CLIENT
+void function SetCachedPilotLoadoutValue( entity player, int loadoutIndex, string loadoutProperty, string value )
+{
+ if ( !IsValidPilotLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Tried to set pilot " + loadoutProperty + " to invalid value: " + value )
+ return
+ }
+
+ // Reset child properties when parent changes
+ ResolveInvalidLoadoutChildValues( player, "pilot", loadoutIndex, loadoutProperty, value )
+
+ #if HAS_THREAT_SCOPE_SLOT_LOCK
+ if ( loadoutProperty.tolower() == "primaryattachment" && value == "threat_scope" )
+ {
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "primaryMod2", "" )
+ }
+ #endif
+
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], loadoutProperty, value )
+ UpdateDerivedPilotLoadoutData( shGlobal.cachedPilotLoadouts[ loadoutIndex ] )
+
+ #if UI
+ if ( value == "" )
+ value = "null"
+ ClientCommand( "SetPersistentLoadoutValue pilot " + loadoutIndex + " " + loadoutProperty + " " + value )
+ #endif // UI
+}
+
+
+void function SetCachedTitanLoadoutValue( entity player, int loadoutIndex, string loadoutProperty, string value )
+{
+ if ( !IsValidTitanLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Tried to set titan " + loadoutProperty + " to invalid value: " + value )
+ return
+ }
+
+ // Reset child properties when parent changes
+ ResolveInvalidLoadoutChildValues( player, "titan", loadoutIndex, loadoutProperty, value )
+
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], loadoutProperty, value )
+ UpdateDerivedTitanLoadoutData( shGlobal.cachedTitanLoadouts[ loadoutIndex ] )
+
+ #if UI
+ if ( value == "" )
+ value = "null"
+ ClientCommand( "SetPersistentLoadoutValue titan " + loadoutIndex + " " + loadoutProperty + " " + value )
+ #endif // UI
+}
+
+
+
+// TODO: If we change a property that has a parent or child relationship, all related properties need updating if invalid
+// A parent change should validate children and set invalid to defaults
+// If a child change is invalid for the parent, it should be changed to a valid default based on the parent
+void function SetCachedLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string value )
+{
+ // Keep CLIENT matching UI
+ #if UI
+ RunClientScript( "SetCachedLoadoutValue", player, loadoutType, loadoutIndex, loadoutProperty, value )
+ #endif // UI
+
+ //printt( "=======================================================================================" )
+ //printt( "SetPersistentLoadoutValue called with loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty, "value:", value )
+ //printl( "script GetPlayerArray()[0].SetPersistentVar( \"" + loadoutType + "Loadouts[" + loadoutIndex + "]." + loadoutProperty + "\", \"" + value + "\" )" )
+ //printt( "=======================================================================================" )
+
+ if ( loadoutType == "pilot" )
+ SetCachedPilotLoadoutValue( player, loadoutIndex, loadoutProperty, value )
+ else if ( loadoutType == "titan" )
+ SetCachedTitanLoadoutValue( player, loadoutIndex, loadoutProperty, value )
+ else
+ CodeWarning( "Unhandled loadoutType: " + loadoutType )
+
+ /*
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+ Assert( loadoutIndex < PersistenceGetArrayCount( loadoutType + "Loadouts" ), "Invalid loadoutIndex: " + loadoutIndex )
+
+ if ( value == "null" || value == "" || value == null )
+ {
+ CodeWarning( "Tried to set " + loadoutType + " " + loadoutProperty + " to invalid value: " + value )
+ return
+ }
+
+ var loadoutPropertyEnum = null
+ if ( loadoutType == "pilot" )
+ {
+ if ( !IsValidPilotLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Invalid pilot loadoutProperty: " + loadoutProperty )
+ return
+ }
+
+ loadoutPropertyEnum = GetPilotLoadoutPropertyEnum( loadoutProperty )
+ }
+ else
+ {
+ if ( !IsValidTitanLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Invalid titan loadoutProperty: " + loadoutProperty )
+ return
+ }
+
+ loadoutPropertyEnum = GetTitanLoadoutPropertyEnum( loadoutProperty )
+ }
+
+ if ( loadoutPropertyEnum == null )
+ {
+ CodeWarning( "Couldn't find loadoutPropertyEnum for " + loadoutType + " " + loadoutProperty )
+ return
+ }
+
+ if ( !PersistenceEnumValueIsValid( loadoutPropertyEnum, value ) )
+ {
+ CodeWarning( loadoutProperty + " value: " + value + " not valid in persistent data!" )
+ return
+ }
+
+ // Only checks primary mods and attachments are valid in itemData and the parent isn't locked
+ if ( !IsLoadoutSubitemValid( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ CodeWarning( loadoutProperty + " value: " + value + " failed IsLoadoutSubitemValid() check! It may not exist in itemData, or it's parent item is locked." )
+ return
+ }
+
+ // Reset child properties when parent changes
+ array<string> childProperties = GetChildLoadoutProperties( loadoutType, loadoutProperty )
+ if ( childProperties.len() )
+ {
+ foreach ( childProperty in childProperties )
+ {
+ Assert( value != "" )
+ string childValue = GetLoadoutChildPropertyDefault( loadoutType, childProperty, value )
+
+ if ( loadoutType == "pilot" )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], childProperty, childValue )
+ else
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], childProperty, childValue )
+ }
+ }
+
+ if ( loadoutType == "pilot" )
+ {
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], loadoutProperty, value )
+ UpdateDerivedPilotLoadoutData( shGlobal.cachedPilotLoadouts[ loadoutIndex ] )
+ }
+ else
+ {
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], loadoutProperty, value )
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( shGlobal.cachedTitanLoadouts[ loadoutIndex ] )
+ }
+
+ if ( value == "" || value == "null" )
+ ClientCommand( "SetPersistentLoadoutValue " + loadoutType + " " + loadoutIndex + " " + loadoutProperty + " " + null )
+ else
+ ClientCommand( "SetPersistentLoadoutValue " + loadoutType + " " + loadoutIndex + " " + loadoutProperty + " " + value )
+ */
+}
+#endif // UI || CLIENT
+
+// SERVER version waits till end of frame to reduce remote calls which can get high when loadouts are first initialized
+// CLIENT and UI versions need to wait for persistent data to be ready
+// Ideally, UI and CLIENT would have a callback that triggered when persistent loadout data changed.
+void function UpdateCachedLoadouts()
+{
+ #if SERVER
+ Signal( level, "EndUpdateCachedLoadouts" )
+ EndSignal( level, "EndUpdateCachedLoadouts" )
+
+ WaitEndFrame()
+
+ array<entity> players = GetPlayerArray()
+
+ foreach ( player in players )
+ {
+ if ( player.p.pilotLoadoutChanged )
+ {
+ Remote_CallFunction_NonReplay( player, "UpdateAllCachedPilotLoadouts" )
+ player.p.pilotLoadoutChanged = false
+ }
+
+ if ( player.p.titanLoadoutChanged )
+ {
+ Remote_CallFunction_NonReplay( player, "UpdateAllCachedTitanLoadouts" )
+ player.p.titanLoadoutChanged = false
+ }
+
+ // TEMP client model update method
+ if ( player.p.pilotModelNeedsUpdate != -1 )
+ {
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdatePilotModel", player.p.pilotModelNeedsUpdate )
+ player.p.pilotModelNeedsUpdate = -1
+ }
+
+ if ( player.p.titanModelNeedsUpdate != -1 )
+ {
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdateTitanModel", player.p.titanModelNeedsUpdate )
+ player.p.titanModelNeedsUpdate = -1
+ }
+ }
+ #elseif UI || CLIENT
+ #if UI
+ entity player = GetUIPlayer()
+ #elseif CLIENT
+ entity player = GetLocalClientPlayer()
+ #endif
+
+ if ( player == null )
+ return
+
+ #if UI
+ EndSignal( uiGlobal.signalDummy, "LevelShutdown" )
+ #endif // UI
+
+ while ( player.GetPersistentVarAsInt( "initializedVersion" ) < PERSISTENCE_INIT_VERSION )
+ {
+ WaitFrame()
+ }
+
+ UpdateAllCachedPilotLoadouts()
+ UpdateAllCachedTitanLoadouts()
+ #if CLIENT
+ Signal( level, "CachedLoadoutsReady" )
+ #endif // CLIENT
+ #endif
+}
+
+#if UI
+ void function InitUISpawnLoadoutIndexes()
+ {
+ EndSignal( uiGlobal.signalDummy, "LevelShutdown" )
+
+ entity player = GetUIPlayer()
+
+ if ( player == null )
+ return
+
+ while ( player.GetPersistentVarAsInt( "initializedVersion" ) < PERSISTENCE_INIT_VERSION )
+ WaitFrame()
+
+ uiGlobal.pilotSpawnLoadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+ uiGlobal.titanSpawnLoadoutIndex = GetPersistentSpawnLoadoutIndex( player, "titan" )
+ }
+#endif // UI
+
+// There's no good way to dynamically reference a struct variable
+string function GetPilotLoadoutValue( PilotLoadoutDef loadout, string property )
+{
+ string value
+ printt( "GetPilotLoadoutValue, property: " + property )
+
+ switch ( property )
+ {
+ case "name":
+ value = loadout.name
+ break
+
+ case "suit":
+ value = loadout.suit
+ break
+
+ case "race":
+ value = loadout.race
+ break
+
+ case "primary":
+ value = loadout.primary
+ break
+
+ case "primaryAttachment":
+ value = loadout.primaryAttachment
+ break
+
+ case "primaryMod1":
+ value = loadout.primaryMod1
+ break
+
+ case "primaryMod2":
+ value = loadout.primaryMod2
+ break
+
+ case "primaryMod3":
+ value = loadout.primaryMod3
+ break
+
+ case "secondary":
+ value = loadout.secondary
+ break
+
+ case "secondaryMod1":
+ value = loadout.secondaryMod1
+ break
+
+ case "secondaryMod2":
+ value = loadout.secondaryMod2
+ break
+
+ case "secondaryMod3":
+ value = loadout.secondaryMod3
+ break
+
+ case "weapon3":
+ value = loadout.weapon3
+ break
+
+ case "weapon3Mod1":
+ value = loadout.weapon3Mod1
+ break
+
+ case "weapon3Mod2":
+ value = loadout.weapon3Mod2
+ break
+
+ case "weapon3Mod3":
+ value = loadout.weapon3Mod3
+ break
+
+ case "special":
+ value = loadout.special
+ break
+
+ case "ordnance":
+ value = loadout.ordnance
+ break
+
+ case "passive1":
+ value = loadout.passive1
+ break
+
+ case "passive2":
+ value = loadout.passive2
+ break
+
+ case "melee":
+ value = loadout.melee
+ break
+
+ case "execution":
+ value = loadout.execution
+ break
+
+ case "skinIndex":
+ value = string( loadout.skinIndex )
+ break
+
+ case "camoIndex":
+ value = string( loadout.camoIndex )
+ break
+
+ case "primarySkinIndex":
+ value = string( loadout.primarySkinIndex )
+ break
+
+ case "primaryCamoIndex":
+ value = string( loadout.primaryCamoIndex )
+ break
+
+ case "secondarySkinIndex":
+ value = string( loadout.secondarySkinIndex )
+ break
+
+ case "secondaryCamoIndex":
+ value = string ( loadout.secondaryCamoIndex )
+ break
+
+ case "weapon3SkinIndex":
+ value = string( loadout.weapon3SkinIndex )
+ break
+
+ case "weapon3CamoIndex":
+ value = string ( loadout.weapon3CamoIndex )
+ break
+
+ default:
+ printt( "Returning blank for property: " + property ) //TODO: This should probably just be an error, but some existing calls depend on blank string being returned
+ value = ""
+ }
+
+ return value
+}
+
+// There's no good way to dynamically reference a struct variable
+// If this starts getting called outside of SetPersistentLoadoutValue(), it needs error checking
+void function SetPilotLoadoutValue( PilotLoadoutDef loadout, string property, string value )
+{
+ switch ( property )
+ {
+ case "name":
+ loadout.name = value
+ break
+
+ case "suit":
+ loadout.suit = value
+ break
+
+ case "execution":
+ loadout.execution = value
+ break
+
+ case "race":
+ loadout.race = value
+ break
+
+ case "primary":
+ loadout.primary = value
+ break
+
+ case "primaryAttachment":
+ loadout.primaryAttachment = value
+ break
+
+ case "primaryMod1":
+ loadout.primaryMod1 = value
+ break
+
+ case "primaryMod2":
+ loadout.primaryMod2 = value
+ break
+
+ case "primaryMod3":
+ loadout.primaryMod3 = value
+ break
+
+ case "secondary":
+ loadout.secondary = value
+ break
+
+ case "secondaryMod1":
+ loadout.secondaryMod1 = value
+ break
+
+ case "secondaryMod2":
+ loadout.secondaryMod2 = value
+ break
+
+ case "secondaryMod3":
+ loadout.secondaryMod3 = value
+ break
+
+ case "weapon3":
+ loadout.weapon3 = value
+ break
+
+ case "weapon3Mod1":
+ loadout.weapon3Mod1 = value
+ break
+
+ case "weapon3Mod2":
+ loadout.weapon3Mod2 = value
+ break
+
+ case "weapon3Mod3":
+ loadout.weapon3Mod3 = value
+ break
+
+ case "special":
+ loadout.special = value
+ break
+
+ case "ordnance":
+ loadout.ordnance = value
+ break
+
+ case "passive1":
+ loadout.passive1 = value
+ break
+
+ case "passive2":
+ loadout.passive2 = value
+ break
+
+ case "melee":
+ loadout.melee = value
+ break
+
+ case "skinIndex":
+ loadout.skinIndex = int( value )
+ break
+
+ case "camoIndex":
+ loadout.camoIndex = int( value )
+ break
+
+ case "primarySkinIndex":
+ loadout.primarySkinIndex = int( value )
+ break
+
+ case "primaryCamoIndex":
+ loadout.primaryCamoIndex = int( value )
+ break
+
+ case "secondarySkinIndex":
+ loadout.secondarySkinIndex = int( value )
+ break
+
+ case "secondaryCamoIndex":
+ loadout.secondaryCamoIndex = int( value )
+ break
+
+ case "weapon3SkinIndex":
+ loadout.weapon3SkinIndex = int( value )
+ break
+
+ case "weapon3CamoIndex":
+ loadout.weapon3CamoIndex = int( value )
+ break
+ }
+}
+
+// There's no good way to dynamically reference a struct variable
+string function GetTitanLoadoutValue( TitanLoadoutDef loadout, string property )
+{
+ string value
+
+ switch ( property )
+ {
+ case "name":
+ value = loadout.name
+ break
+
+ case "titanClass":
+ value = loadout.titanClass
+ break
+
+ case "primeTitanRef":
+ value = loadout.primeTitanRef
+ break
+
+ case "setFile":
+ value = loadout.setFile
+ break
+
+ case "coreAbility":
+ value = loadout.coreAbility
+ break
+
+ case "primary":
+ value = loadout.primary
+ break
+
+ case "primaryMod":
+ value = loadout.primaryMod
+ break
+
+ case "special":
+ value = loadout.special
+ break
+
+ case "antirodeo":
+ value = loadout.antirodeo
+ break
+
+ case "ordnance":
+ value = loadout.ordnance
+ break
+
+ case "passive1":
+ value = loadout.passive1
+ break
+
+ case "passive2":
+ value = loadout.passive2
+ break
+
+ case "passive3":
+ value = loadout.passive3
+ break
+
+ case "passive4":
+ value = loadout.passive4
+ break
+
+ case "passive5":
+ value = loadout.passive5
+ break
+
+ case "passive6":
+ value = loadout.passive6
+ break
+
+ case "skinIndex":
+ value = string( loadout.skinIndex )
+ break
+
+ case "camoIndex":
+ value = string ( loadout.camoIndex )
+ break
+
+ case "decalIndex":
+ value = string( loadout.decalIndex )
+ break
+
+ case "primarySkinIndex":
+ value = string ( loadout.primarySkinIndex )
+ break
+
+ case "primaryCamoIndex":
+ value = string ( loadout.primaryCamoIndex )
+ break
+
+ case "difficulty":
+ value = string ( loadout.difficulty ) //TODO: Unused?
+ break
+
+ case "isPrime":
+ value = loadout.isPrime
+ break
+
+ case "primeSkinIndex":
+ value = string( loadout.primeSkinIndex )
+ break
+
+ case "primeCamoIndex":
+ value = string( loadout.primeCamoIndex )
+ break
+
+ case "primeDecalIndex":
+ value = string( loadout.primeDecalIndex )
+ break
+
+ case "titanExecution":
+ value = loadout.titanExecution
+ break
+
+ default:
+ printt( "Returning blank for property: " + property ) //TODO: This should probably just be an error, but some existing calls depend on blank string being returned
+ value = ""
+ }
+
+ return value
+}
+
+// There's no good way to dynamically reference a struct variable
+// If this starts getting called outside of SetPersistentLoadoutValue(), it needs error checking
+void function SetTitanLoadoutValue( TitanLoadoutDef loadout, string property, string value )
+{
+ switch ( property )
+ {
+ case "name":
+ loadout.name = value
+ break
+
+ case "setFile":
+ loadout.setFile = value
+ break
+
+ case "primaryMod":
+ loadout.primaryMod = value
+ break
+
+ case "special":
+ loadout.special = value
+ break
+
+ case "antirodeo":
+ loadout.antirodeo = value
+ break
+
+ case "passive1":
+ loadout.passive1 = value
+ break
+
+ case "passive2":
+ loadout.passive2 = value
+ break
+
+ case "passive3":
+ loadout.passive3 = value
+ break
+
+ case "passive4":
+ loadout.passive4 = value
+ break
+
+ case "passive5":
+ loadout.passive5 = value
+ break
+
+ case "passive6":
+ loadout.passive6 = value
+ break
+
+ case "skinIndex":
+ loadout.skinIndex = int( value )
+ break
+
+ case "camoIndex":
+ loadout.camoIndex = int( value )
+ break
+
+ case "decalIndex":
+ loadout.decalIndex = int( value )
+ break
+
+ case "primarySkinIndex":
+ loadout.primarySkinIndex = int( value )
+ break
+
+ case "primaryCamoIndex":
+ loadout.primaryCamoIndex = int( value )
+ break
+
+ case "isPrime":
+ loadout.isPrime = value
+ break
+
+ case "primeSkinIndex":
+ loadout.primeSkinIndex = int( value )
+ break
+
+ case "primeCamoIndex":
+ loadout.primeCamoIndex = int( value )
+ break
+
+ case "primeDecalIndex":
+ loadout.primeDecalIndex = int( value )
+ break
+
+ case "titanExecution":
+ loadout.titanExecution = value
+ break
+
+ case "showArmBadge":
+ loadout.showArmBadge = int( value )
+ break
+ }
+}
+
+bool function IsValidPilotLoadoutProperty( string propertyName )
+{
+ switch ( propertyName )
+ {
+ case "name":
+ case "suit":
+ case "race":
+ case "execution":
+ case "primary":
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondary":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ case "ordnance":
+ case "special":
+ case "passive1":
+ case "passive2":
+ case "melee":
+ case "skinIndex":
+ case "camoIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ return true
+ }
+
+ return false
+}
+
+bool function IsValidTitanLoadoutProperty( string propertyName )
+{
+ switch ( propertyName )
+ {
+ case "name":
+ case "titanClass":
+ case "setFile":
+ case "primaryMod":
+ case "special":
+ case "antirodeo":
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "isPrime":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ case "titanExecution":
+ case "showArmBadge":
+ return true
+ }
+
+ return false
+}
+
+var function GetPilotLoadoutPropertyEnum( string property )
+{
+ switch ( property )
+ {
+ case "suit":
+ return "pilotSuit"
+
+ case "race":
+ return "pilotRace"
+
+ case "primary":
+ case "secondary":
+ case "weapon3":
+ case "special":
+ case "ordnance":
+ case "melee":
+ return "loadoutWeaponsAndAbilities"
+
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ return "pilotMod"
+
+ case "passive1":
+ case "passive2":
+ return "pilotPassive"
+
+ case "name":
+ default:
+ return null
+ }
+}
+
+var function GetTitanLoadoutPropertyEnum( string property )
+{
+ switch ( property )
+ {
+ case "titanClass":
+ return "titanClasses"
+ case "primary":
+ case "special":
+ case "antirodeo":
+ case "ordnance":
+ return "loadoutWeaponsAndAbilities"
+ case "primaryMod":
+ return "titanMod"
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ return "titanPassive"
+ case "isPrime":
+ return "titanIsPrimeTitan"
+ case "name":
+ default:
+ return null
+ }
+}
+
+int function GetItemTypeFromPilotLoadoutProperty( string loadoutProperty )
+{
+ int itemType
+
+ switch ( loadoutProperty )
+ {
+ case "suit":
+ itemType = eItemTypes.PILOT_SUIT
+ break
+
+ case "primary":
+ itemType = eItemTypes.PILOT_PRIMARY
+ break
+
+ case "secondary":
+ case "weapon3": // Most cases need to treat as secondary, so don't return eItemTypes.PILOT_WEAPON3 here
+ itemType = eItemTypes.PILOT_SECONDARY
+ break
+
+ case "special":
+ itemType = eItemTypes.PILOT_SPECIAL
+ break
+
+ case "ordnance":
+ itemType = eItemTypes.PILOT_ORDNANCE
+ break
+
+ case "passive1":
+ itemType = eItemTypes.PILOT_PASSIVE1
+ break
+
+ case "passive2":
+ itemType = eItemTypes.PILOT_PASSIVE2
+ break
+
+ case "primaryAttachment":
+ itemType = eItemTypes.PILOT_PRIMARY_ATTACHMENT
+ break
+
+ case "primaryMod1":
+ case "primaryMod2":
+ itemType = eItemTypes.PILOT_PRIMARY_MOD
+ break
+
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ itemType = eItemTypes.PILOT_SECONDARY_MOD
+ break
+
+ case "primaryMod3":
+ case "secondaryMod3":
+ case "weapon3Mod3":
+ itemType = eItemTypes.PILOT_WEAPON_MOD3
+ break
+
+ case "race":
+ itemType = eItemTypes.RACE
+ break
+
+ case "execution":
+ itemType = eItemTypes.PILOT_EXECUTION
+ break
+
+ default:
+ Assert( false, "Invalid pilot loadout property!" )
+ }
+
+ return itemType
+}
+
+int function GetItemTypeFromTitanLoadoutProperty( string loadoutProperty, string setFile = "" )
+{
+ int itemType
+
+ switch ( loadoutProperty )
+ {
+ case "setFile":
+ itemType = eItemTypes.TITAN
+ break
+
+ case "coreAbility":
+ itemType = eItemTypes.TITAN_CORE_ABILITY
+ break
+
+ case "primary":
+ itemType = eItemTypes.TITAN_PRIMARY
+ break
+
+ case "special":
+ itemType = eItemTypes.TITAN_SPECIAL
+ break
+
+ case "ordnance":
+ itemType = eItemTypes.TITAN_ORDNANCE
+ break
+
+ case "antirodeo":
+ itemType = eItemTypes.TITAN_ANTIRODEO
+ break
+
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ Assert( setFile != "" )
+ itemType = GetTitanLoadoutPropertyPassiveType( setFile, loadoutProperty )
+ break
+
+ case "titanExecution":
+ Assert( setFile != "" )
+ itemType = GetTitanLoadoutPropertyExecutionType( setFile, loadoutProperty )
+ break
+
+ case "voice":
+ itemType = eItemTypes.TITAN_OS
+ break
+
+ case "primaryMod":
+ itemType = eItemTypes.TITAN_PRIMARY_MOD
+ break
+
+ default:
+ Assert( false, "Invalid titan loadout property!" )
+ }
+
+ return itemType
+}
+
+string function GetLoadoutChildPropertyDefault( string loadoutType, string propertyName, string parentValue )
+{
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ string resetValue = ""
+
+ if ( loadoutType == "pilot" )
+ {
+ switch ( propertyName )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ resetValue = GetWeaponBasedDefaultMod( parentValue, propertyName )
+ break
+
+ case "primaryCamoIndex":
+ case "primarySkinIndex":
+ case "secondaryCamoIndex":
+ case "secondarySkinIndex":
+ case "weapon3CamoIndex":
+ case "weapon3SkinIndex":
+ resetValue = "0"
+ break
+ }
+ }
+
+ return resetValue
+}
+
+// There's no great way to know the dependency heirarchy of persistent loadout data
+array<string> function GetChildLoadoutProperties( string loadoutType, string propertyName )
+{
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ array<string> childProperties
+
+ if ( loadoutType == "pilot" )
+ {
+ switch ( propertyName )
+ {
+ /*case "suit":
+ childProperties.append( "passive1" )
+ childProperties.append( "passive2" )
+ break*/
+
+ case "primary":
+ childProperties.append( "primaryAttachment" )
+ childProperties.append( "primaryMod1" )
+ childProperties.append( "primaryMod2" )
+ childProperties.append( "primaryMod3" )
+ childProperties.append( "primaryCamoIndex" )
+ childProperties.append( "primarySkinIndex" )
+ break
+
+ case "secondary":
+ childProperties.append( "secondaryMod1" )
+ childProperties.append( "secondaryMod2" )
+ childProperties.append( "secondaryMod3" )
+ childProperties.append( "secondaryCamoIndex" )
+ childProperties.append( "secondarySkinIndex" )
+ break
+
+ case "weapon3":
+ childProperties.append( "weapon3Mod1" )
+ childProperties.append( "weapon3Mod2" )
+ childProperties.append( "weapon3Mod3" )
+ childProperties.append( "weapon3CamoIndex" )
+ childProperties.append( "weapon3SkinIndex" )
+ break
+
+ /*case "melee":
+ childProperties.append( "meleeMods" ) // not in persistent data
+ break
+
+ case "ordnance":
+ childProperties.append( "ordnanceMods" ) // not in persistent data
+ break*/
+ }
+ }
+ else
+ {
+ switch ( propertyName )
+ {
+ /*case "setFile":
+ childProperties.append( "setFileMods" ) // not in persistent data
+ break
+
+ case "primary":
+ //childProperties.append( "primaryAttachment" ) // not in persistent data
+ childProperties.append( "primaryMod" )
+ break
+
+ case "special":
+ childProperties.append( "specialMods" ) // not in persistent data
+ break
+
+ case "ordnance":
+ childProperties.append( "ordnanceMods" ) // not in persistent data
+ break
+
+ case "antirodeo":
+ childProperties.append( "antirodeoMods" ) // not in persistent data
+ break*/
+ }
+ }
+
+ return childProperties
+}
+
+// There's no great way to know the dependency heirarchy of persistent loadout data
+string function GetParentLoadoutProperty( string loadoutType, string propertyName )
+{
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ string parentProperty
+
+ if ( loadoutType == "pilot" )
+ {
+ switch ( propertyName )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "primaryCamoIndex":
+ case "primarySkinIndex":
+ parentProperty = "primary"
+ break
+
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "secondaryCamoIndex":
+ case "secondarySkinIndex":
+ parentProperty = "secondary"
+ break
+
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ case "weapon3CamoIndex":
+ case "weapon3SkinIndex":
+ parentProperty = "weapon3"
+ break
+
+ case "passive1":
+ case "passive2":
+ parentProperty = "special"
+ break
+
+ default:
+ Assert( 0, "Unknown loadout propertyName: " + propertyName )
+ }
+ }
+ else
+ {
+ switch ( propertyName )
+ {
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ parentProperty = "titanClass"
+ break
+
+ default:
+ Assert( 0, "Unknown loadout propertyName: " + propertyName )
+ }
+ }
+
+ return parentProperty
+}
+
+int function GetPersistentSpawnLoadoutIndex( entity player, string loadoutType )
+{
+ int loadoutIndex = player.GetPersistentVarAsInt( loadoutType + "SpawnLoadout.index" )
+ if ( loadoutType == "titan" && loadoutIndex >= NUM_PERSISTENT_TITAN_LOADOUTS )
+ loadoutIndex = 0
+
+ return loadoutIndex
+}
+
+#if SERVER
+void function SetPersistentSpawnLoadoutIndex( entity player, string loadoutType, int loadoutIndex )
+{
+ Assert( loadoutIndex >= 0 )
+ player.SetPersistentVar( loadoutType + "SpawnLoadout.index", loadoutIndex )
+}
+#endif // SERVER
+
+#if UI
+ void function SetEditLoadout( string loadoutType, int loadoutIndex )
+ {
+ uiGlobal.editingLoadoutType = loadoutType
+ uiGlobal.editingLoadoutIndex = loadoutIndex
+ }
+
+ void function ClearEditLoadout()
+ {
+ uiGlobal.editingLoadoutType = ""
+ uiGlobal.editingLoadoutIndex = -1
+ }
+
+ PilotLoadoutDef function GetPilotEditLoadout()
+ {
+ return GetCachedPilotLoadout( uiGlobal.editingLoadoutIndex )
+ }
+
+ TitanLoadoutDef function GetTitanEditLoadout()
+ {
+ return GetCachedTitanLoadout( uiGlobal.editingLoadoutIndex )
+ }
+
+ int function GetLoadoutIndexForTitanClass( string titanClass )
+ {
+ for ( int loadoutIndex = 0; loadoutIndex < NUM_PERSISTENT_TITAN_LOADOUTS; loadoutIndex++ )
+ {
+ TitanLoadoutDef loadout = GetCachedTitanLoadout( loadoutIndex )
+
+ if ( loadout.titanClass == titanClass )
+ return loadoutIndex
+ }
+
+ return -1
+ }
+
+ string function GetPilotLoadoutName( PilotLoadoutDef loadout )
+ {
+ if ( IsTokenLoadoutName( loadout.name ) )
+ return Localize( loadout.name )
+
+ return loadout.name
+ }
+
+ string function GetTitanLoadoutName( TitanLoadoutDef loadout )
+ {
+ if ( IsTokenLoadoutName( loadout.name ) )
+ return Localize( loadout.name )
+
+ return loadout.name
+ }
+
+ void function SetTextFromItemName( var element, string ref )
+ {
+ string text = ""
+
+ if ( ref != "" )
+ text = GetItemName( ref )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromItemDescription( var element, string ref )
+ {
+ string text = ""
+
+ if ( ref != "" )
+ text = GetItemDescription( ref )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromItemLongDescription( var element, string ref )
+ {
+ string text = ""
+
+ if ( ref != "" )
+ text = GetItemLongDescription( ref )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetImageFromItemImage( var element, string ref )
+ {
+ if ( ref != "" )
+ {
+ Hud_SetImage( element, GetItemImage( ref ) )
+ Hud_Show( element )
+ }
+ else
+ {
+ Hud_Hide( element )
+ }
+ }
+
+ void function SetTextFromSubItemClipSize( var element, string ref, string modRef )
+ {
+ Hud_SetText( element, "" )
+ if ( ref != "" && modRef != "" )
+ {
+ int clipDiff = GetSubItemClipSizeStat( ref, modRef )
+ if ( clipDiff == 0 )
+ return
+
+ if ( clipDiff > 0 )
+ {
+ Hud_SetColor( element, 141, 197, 84, 255 )
+ Hud_SetText( element, "#MOD_CLIP_AMMO_INCREASE", string( clipDiff ) )
+ }
+ else
+ {
+ Hud_SetColor( element, 211, 77, 61, 255 )
+ Hud_SetText( element, "#MOD_CLIP_AMMO_DECREASE", string( abs( clipDiff ) ) )
+ }
+ }
+ }
+
+ void function SetTextFromSubitemName( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetSubitemName( parentRef, childRef )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromSubitemDescription( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetSubitemDescription( parentRef, childRef )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromSubitemLongDescription( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetSubitemLongDescription( parentRef, childRef )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetImageFromSubitemImage( var element, string parentRef, string childRef, asset defaultIcon = $"" )
+ {
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ {
+ Hud_SetImage( element, GetSubitemImage( parentRef, childRef ) )
+ Hud_Show( element )
+ }
+ else
+ {
+ if ( defaultIcon != $"" )
+ {
+ Hud_SetImage( element, defaultIcon )
+ Hud_Show( element )
+ }
+ else
+ {
+ Hud_Hide( element )
+ }
+ }
+ }
+
+ void function SetTextFromSubitemUnlockReq( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetItemUnlockReqText( childRef, parentRef )
+
+ Hud_SetText( element, text )
+ }
+#endif //UI
+
+#if UI || CLIENT
+ void function UpdateAllCachedPilotLoadouts()
+ {
+ int numLoadouts = shGlobal.cachedPilotLoadouts.len()
+
+ for ( int i = 0; i < numLoadouts; i++ )
+ UpdateCachedPilotLoadout( i )
+ }
+
+ void function UpdateAllCachedTitanLoadouts()
+ {
+ int numLoadouts = shGlobal.cachedTitanLoadouts.len()
+
+ for ( int i = 0; i < numLoadouts; i++ )
+ UpdateCachedTitanLoadout( i )
+ }
+
+ void function UpdateCachedPilotLoadout( int loadoutIndex )
+ {
+ entity player
+ #if UI
+ player = GetUIPlayer()
+ #elseif CLIENT
+ player = GetLocalClientPlayer()
+ #endif
+
+ if ( player == null )
+ return
+
+ PopulatePilotLoadoutFromPersistentData( player, shGlobal.cachedPilotLoadouts[ loadoutIndex ], loadoutIndex )
+ }
+
+ void function UpdateCachedTitanLoadout( int loadoutIndex )
+ {
+ entity player
+ #if UI
+ player = GetUIPlayer()
+ #elseif CLIENT
+ player = GetLocalClientPlayer()
+ #endif
+
+ if ( player == null )
+ return
+
+ PopulateTitanLoadoutFromPersistentData( player, shGlobal.cachedTitanLoadouts[ loadoutIndex ], loadoutIndex )
+ }
+
+ PilotLoadoutDef function GetCachedPilotLoadout( int loadoutIndex )
+ {
+ Assert( loadoutIndex >= 0 && loadoutIndex < shGlobal.cachedPilotLoadouts.len() )
+
+ return shGlobal.cachedPilotLoadouts[ loadoutIndex ]
+ }
+
+ TitanLoadoutDef function GetCachedTitanLoadout( int loadoutIndex )
+ {
+ Assert( loadoutIndex >= 0 && loadoutIndex < shGlobal.cachedTitanLoadouts.len() )
+
+ return shGlobal.cachedTitanLoadouts[ loadoutIndex ]
+ }
+
+ PilotLoadoutDef[ NUM_PERSISTENT_PILOT_LOADOUTS ] function GetAllCachedPilotLoadouts()
+ {
+ return shGlobal.cachedPilotLoadouts
+ }
+
+ TitanLoadoutDef[ NUM_PERSISTENT_TITAN_LOADOUTS ] function GetAllCachedTitanLoadouts()
+ {
+ return shGlobal.cachedTitanLoadouts
+ }
+
+ int function GetCachedTitanLoadoutCamoIndex( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanCamoIndexFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ int function GetCachedTitanLoadoutSkinIndex( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanSkinIndexFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ int function GetCachedTitanLoadoutDecalIndex( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanDecalIndexFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ asset function GetCachedTitanLoadoutArmBadge( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanArmBadgeFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ int function GetCachedTitanArmBadgeState( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return cachedTitanLoadout.showArmBadge
+ }
+
+#endif // UI || CLIENT
+
+#if UI
+ bool function IsTokenLoadoutName( string name )
+ {
+ if ( name.find( "#DEFAULT_PILOT_" ) != null || name.find( "#DEFAULT_TITAN_" ) != null )
+ return true
+
+ return false
+ }
+#endif // UI
+
+string function Loadouts_GetSetFileForRequestedClass( entity player )
+{
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout.setFile
+}
+
+#if SERVER
+ void function SetPersistentPilotLoadout( entity player, int loadoutIndex, PilotLoadoutDef loadout )
+ {
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "name", loadout.name )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "suit", loadout.suit )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "race", loadout.race )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primary", loadout.primary )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryAttachment", loadout.primaryAttachment )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod1", loadout.primaryMod1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod2", loadout.primaryMod2 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod3", loadout.primaryMod3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondary", loadout.secondary )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod1", loadout.secondaryMod1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod2", loadout.secondaryMod2 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod3", loadout.secondaryMod3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3", loadout.weapon3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod1", loadout.weapon3Mod1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod2", loadout.weapon3Mod2 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod3", loadout.weapon3Mod3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "ordnance", loadout.ordnance )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive1", loadout.passive1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive2", loadout.passive2 )
+ }
+
+ void function SetPersistentTitanLoadout( entity player, int loadoutIndex, TitanLoadoutDef loadout )
+ {
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "name", loadout.name )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass", GetTitanCharacterNameFromSetFile( loadout.setFile ) )
+ //SetPersistentLoadoutValue( player, "titan", loadoutIndex, "setFile", loadout.setFile )
+ //SetPersistentLoadoutValue( player, "titan", loadoutIndex, "primaryMod", loadout.primaryMod )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "special", loadout.special )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "antirodeo", loadout.antirodeo )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive1", loadout.passive1 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive2", loadout.passive2 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive3", loadout.passive3 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive4", loadout.passive4 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive5", loadout.passive5 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive6", loadout.passive6 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanExecution", loadout.titanExecution )
+ }
+
+ bool function PlayerIsInGracePeriod( entity player )
+ {
+ if ( svGlobal.isInPilotGracePeriod && !player.IsTitan() )
+ return true
+
+ if ( player.s.inGracePeriod )
+ return true
+
+ if ( player.p.usingLoadoutCrate )
+ return true
+
+ return false
+ }
+
+ bool function Loadouts_CanGivePilotLoadout( entity player )
+ {
+ if ( !IsAlive( player ) )
+ return false
+
+ if ( !PlayerIsInGracePeriod( player ) )
+ return false
+
+ // hack for bug 114632, 167264. Real fix would be to make dropship spawn script not end on anim reset from model change.
+ if ( player.GetParent() != null )
+ {
+ if ( HasCinematicFlag( player, CE_FLAG_INTRO ) )
+ return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) )
+ return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_WAVE_SPAWNING ) )
+ return false
+ }
+
+ if ( player.IsTitan() )
+ return false
+
+ return true
+ }
+
+ bool function Loadouts_CanGiveTitanLoadout( entity player )
+ {
+ // if ( GetPlayerBurnCardOnDeckIndex( player ) != null )
+ // return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_INTRO ) )
+ return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) )
+ return false
+
+ if ( !IsAlive( player ) )
+ return false
+
+ if ( !PlayerIsInGracePeriod( player ) )
+ return false
+
+ if ( player.isSpawningHotDroppingAsTitan )
+ return false
+
+ if ( !player.IsTitan() )
+ return false
+
+ // JFS: wargames round switch BS
+ if ( !IsValid( player.GetTitanSoul() ) )
+ return false
+
+ return true
+ }
+
+
+ string function Loadouts_GetSetFileForActiveClass( entity player )
+ {
+ int loadoutIndex = GetActivePilotLoadoutIndex( player )
+
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout.setFile
+ }
+
+
+ string function Loadouts_GetPilotRaceForActiveClass( entity player )
+ {
+ int loadoutIndex = GetActivePilotLoadoutIndex( player )
+
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout.race
+ }
+
+ bool function Loadouts_TryGivePilotLoadout( entity player )
+ {
+ if ( !Loadouts_CanGivePilotLoadout( player ) )
+ return false
+
+ PilotLoadoutDef loadout
+
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ #if DEV
+ if ( player.IsBot() && !player.IsPlayback() && GetConVarString( "bot_pilot_settings" ) == "random" )
+ loadout = GetRandomPilotLoadout()
+ else
+ #endif
+ loadout = GetPilotLoadoutFromPersistentData( player, loadoutIndex )
+
+ UpdateDerivedPilotLoadoutData( loadout )
+
+ if ( player.IsBot() && !player.IsPlayback() )
+ OverrideBotPilotLoadout( loadout )
+
+ GivePilotLoadout( player, loadout )
+ SetActivePilotLoadout( player )
+ SetActivePilotLoadoutIndex( player, loadoutIndex )
+
+ //PROTO_DisplayPilotLoadouts( player, loadout )
+
+ return true
+ }
+
+ bool function Loadouts_TryGiveTitanLoadout( entity player )
+ {
+ if ( !Loadouts_CanGiveTitanLoadout( player ) )
+ return false
+
+ Assert( IsMultiplayer(), "Spawning as a Titan is not supported in SP currently" )
+
+ entity soul = player.GetTitanSoul()
+
+ TakeAllWeapons( player )
+ TakeAllPassives( player )
+
+ soul.passives = arrayofsize( GetNumPassives(), false ) //Clear out passives on soul
+
+ TitanLoadoutDef loadout
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "titan" )
+
+ #if DEV
+ if ( player.IsBot() && !player.IsPlayback() )
+ {
+ string botTitanSettings = GetConVarString( "bot_titan_settings" )
+ loadout = GetRandomTitanLoadout( botTitanSettings )
+
+ array<string> legalLoadouts = GetAllowedTitanSetFiles()
+ if ( legalLoadouts.contains( botTitanSettings ) )
+ loadout.setFile = botTitanSettings //Overwrite just the setfile, mods etc will be random
+ }
+ else
+ #endif
+ loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
+
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout )
+
+ if ( player.IsBot() && !player.IsPlayback() )
+ OverrideBotTitanLoadout( loadout )
+
+ ApplyTitanLoadoutModifiers( player, loadout )
+
+ player.SetPlayerSettingsFromDataTable( { playerSetFile = loadout.setFile, playerSetFileMods = loadout.setFileMods } )
+ GiveTitanLoadout( player, loadout )
+ SetActiveTitanLoadoutIndex( player, loadoutIndex )
+ SetActiveTitanLoadout( player )
+ //PROTO_DisplayTitanLoadouts( player, player, loadout )
+
+ string settings = GetSoulPlayerSettings( soul )
+ var titanTint = Dev_GetPlayerSettingByKeyField_Global( settings, "titan_tint" )
+
+ if ( titanTint != null )
+ {
+ expect string( titanTint )
+ Highlight_SetEnemyHighlight( player, titanTint )
+ }
+ else
+ {
+ Highlight_ClearEnemyHighlight( player )
+ }
+
+ var title = GetPlayerSettingsFieldForClassName( settings, "printname" )
+ if ( title != null )
+ {
+ player.SetTitle( expect string( title ) )
+ }
+
+ return true
+ }
+
+ void function OverrideBotPilotLoadout( PilotLoadoutDef loadout )
+ {
+ string bot_force_pilot_primary = GetConVarString( "bot_force_pilot_primary" )
+ string bot_force_pilot_secondary = GetConVarString( "bot_force_pilot_secondary" )
+ //string bot_force_pilot_weapon3 = GetConVarString( "bot_force_pilot_weapon3" ) // TODO: Convar needs to be added in code
+ string bot_force_pilot_ordnance = GetConVarString( "bot_force_pilot_ordnance" )
+ string bot_force_pilot_ability = GetConVarString( "bot_force_pilot_ability" )
+
+ // Primary
+ if ( DevFindItemByName( eItemTypes.PILOT_PRIMARY, bot_force_pilot_primary ) )
+ {
+ loadout.primary = bot_force_pilot_primary
+ loadout.primaryAttachment = ""
+ loadout.primaryAttachments = []
+ loadout.primaryMod1 = ""
+ loadout.primaryMod2 = ""
+ loadout.primaryMod3 = ""
+ loadout.primaryMods = []
+ }
+
+ // Secondary
+ if ( DevFindItemByName( eItemTypes.PILOT_SECONDARY, bot_force_pilot_secondary ) )
+ {
+ loadout.secondary = bot_force_pilot_secondary
+ loadout.secondaryMod1 = ""
+ loadout.secondaryMod2 = ""
+ loadout.secondaryMod3 = ""
+ loadout.secondaryMods = []
+ }
+
+ // Weapon3
+ //if ( DevFindItemByName( eItemTypes.PILOT_SECONDARY, bot_force_pilot_weapon3 ) )
+ //{
+ // loadout.weapon3 = bot_force_pilot_weapon3
+ // loadout.weapon3Mod1 = ""
+ // loadout.weapon3Mod2 = ""
+ // loadout.weapon3Mod3 = ""
+ // loadout.weapon3Mods = []
+ //}
+
+ // Ordnance/Offhand
+ if ( DevFindItemByName( eItemTypes.PILOT_ORDNANCE, bot_force_pilot_ordnance ) )
+ {
+ loadout.ordnance = bot_force_pilot_ordnance
+ loadout.ordnanceMods = []
+ }
+
+ // Ability/Special
+ if ( DevFindItemByName( eItemTypes.PILOT_SPECIAL, bot_force_pilot_ability ) )
+ {
+ loadout.special = bot_force_pilot_ability
+ loadout.specialMods = []
+ }
+ }
+
+ void function OverrideBotTitanLoadout( TitanLoadoutDef loadout )
+ {
+ string bot_force_titan_primary = GetConVarString( "bot_force_titan_primary" )
+ string bot_force_titan_ordnance = GetConVarString( "bot_force_titan_ordnance" )
+ string bot_force_titan_ability = GetConVarString( "bot_force_titan_ability" )
+
+ // Primary
+ if ( DevFindItemByName( eItemTypes.TITAN_PRIMARY, bot_force_titan_primary ) )
+ {
+ loadout.primary = bot_force_titan_primary
+ loadout.primaryMod = ""
+ loadout.primaryMods = []
+ }
+
+ // Ordnance/Offhand
+ if ( DevFindItemByName( eItemTypes.TITAN_ORDNANCE, bot_force_titan_ordnance ) )
+ {
+ loadout.ordnance = bot_force_titan_ordnance
+ loadout.ordnanceMods = []
+ }
+
+ // Ability/Special
+ if ( DevFindItemByName( eItemTypes.TITAN_SPECIAL, bot_force_titan_ability ) )
+ {
+ loadout.special = bot_force_titan_ability
+ loadout.specialMods = []
+ }
+ }
+
+ TitanLoadoutDef function GetTitanSpawnLoadout( entity player )
+ {
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "titan" )
+ TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout, player )
+
+ return loadout
+ }
+
+ // TODO: make loadout crate stuff not update your requested loadout
+ void function Loadouts_OnUsedLoadoutCrate( entity player )
+ {
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, loadoutIndex )
+
+ GivePilotLoadout( player, loadout )
+ }
+
+ void function SetActivePilotLoadout( entity player )
+ {
+ PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, GetPersistentSpawnLoadoutIndex( player, "pilot" ) )
+
+ player.SetPersistentVar( "activePilotLoadout.name", loadout.name )
+ player.SetPersistentVar( "activePilotLoadout.suit", loadout.suit )
+ player.SetPersistentVar( "activePilotLoadout.race", loadout.race )
+ player.SetPersistentVar( "activePilotLoadout.execution", loadout.execution )
+ player.SetPersistentVar( "activePilotLoadout.primary", loadout.primary )
+ player.SetPersistentVar( "activePilotLoadout.primaryAttachment", loadout.primaryAttachment )
+ player.SetPersistentVar( "activePilotLoadout.primaryMod1", loadout.primaryMod1 )
+ player.SetPersistentVar( "activePilotLoadout.primaryMod2", loadout.primaryMod2 )
+ player.SetPersistentVar( "activePilotLoadout.primaryMod3", loadout.primaryMod3 )
+ player.SetPersistentVar( "activePilotLoadout.secondary", loadout.secondary )
+ player.SetPersistentVar( "activePilotLoadout.secondaryMod1", loadout.secondaryMod1 )
+ player.SetPersistentVar( "activePilotLoadout.secondaryMod2", loadout.secondaryMod2 )
+ player.SetPersistentVar( "activePilotLoadout.secondaryMod3", loadout.secondaryMod3 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3", loadout.weapon3 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3Mod1", loadout.weapon3Mod1 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3Mod2", loadout.weapon3Mod2 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3Mod3", loadout.weapon3Mod3 )
+ player.SetPersistentVar( "activePilotLoadout.ordnance", loadout.ordnance )
+ player.SetPersistentVar( "activePilotLoadout.passive1", loadout.passive1 )
+ player.SetPersistentVar( "activePilotLoadout.passive2", loadout.passive2 )
+ player.SetPersistentVar( "activePilotLoadout.skinIndex", loadout.skinIndex )
+ player.SetPersistentVar( "activePilotLoadout.camoIndex", loadout.camoIndex )
+ player.SetPersistentVar( "activePilotLoadout.primarySkinIndex", loadout.primarySkinIndex )
+ player.SetPersistentVar( "activePilotLoadout.primaryCamoIndex", loadout.primaryCamoIndex )
+ player.SetPersistentVar( "activePilotLoadout.secondarySkinIndex", loadout.secondarySkinIndex )
+ player.SetPersistentVar( "activePilotLoadout.secondaryCamoIndex", loadout.secondaryCamoIndex )
+ player.SetPersistentVar( "activePilotLoadout.weapon3SkinIndex", loadout.weapon3SkinIndex )
+ player.SetPersistentVar( "activePilotLoadout.weapon3CamoIndex", loadout.weapon3CamoIndex )
+ }
+
+ void function SetActivePilotLoadoutIndex( entity player, int loadoutIndex )
+ {
+ player.p.activePilotLoadoutIndex = loadoutIndex
+ player.SetPlayerNetInt( "activePilotLoadoutIndex", loadoutIndex )
+ }
+
+ void function SetActiveTitanLoadout( entity player )
+ {
+ Assert( player.IsPlayer(), "Titan spawn loadout makes sense for players not NPCs")
+ TitanLoadoutDef loadout = GetTitanSpawnLoadout( player )
+
+ player.SetPersistentVar( "activeTitanLoadout.name", loadout.name )
+ player.SetPersistentVar( "activeTitanLoadout.titanClass", loadout.titanClass )
+ player.SetPersistentVar( "activeTitanLoadout.primaryMod", loadout.primaryMod )
+ player.SetPersistentVar( "activeTitanLoadout.special", loadout.special )
+ player.SetPersistentVar( "activeTitanLoadout.antirodeo", loadout.antirodeo )
+ player.SetPersistentVar( "activeTitanLoadout.passive1", loadout.passive1 )
+ player.SetPersistentVar( "activeTitanLoadout.passive2", loadout.passive2 )
+ player.SetPersistentVar( "activeTitanLoadout.passive3", loadout.passive3 )
+ player.SetPersistentVar( "activeTitanLoadout.passive4", loadout.passive4 )
+ player.SetPersistentVar( "activeTitanLoadout.passive5", loadout.passive5 )
+ player.SetPersistentVar( "activeTitanLoadout.passive6", loadout.passive6 )
+ player.SetPersistentVar( "activeTitanLoadout.skinIndex", loadout.skinIndex )
+ player.SetPersistentVar( "activeTitanLoadout.camoIndex", loadout.camoIndex )
+ player.SetPersistentVar( "activeTitanLoadout.decalIndex", loadout.decalIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primarySkinIndex", loadout.primarySkinIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primaryCamoIndex", loadout.primaryCamoIndex )
+ player.SetPersistentVar( "activeTitanLoadout.titanExecution", loadout.titanExecution )
+ player.SetPersistentVar( "activeTitanLoadout.isPrime", loadout.isPrime )
+ player.SetPersistentVar( "activeTitanLoadout.primeSkinIndex", loadout.primeSkinIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primeCamoIndex", loadout.primeCamoIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primeDecalIndex", loadout.primeDecalIndex )
+ player.SetPersistentVar( "activeTitanLoadout.showArmBadge", loadout.showArmBadge )
+ }
+
+ void function SetActiveTitanLoadoutIndex( entity player, int loadoutIndex )
+ {
+ //printt( ">>>>>>>>>>>>> SetActiveTitanLoadoutIndex() with index:", loadoutIndex )
+ player.p.activeTitanLoadoutIndex = loadoutIndex
+ player.SetPlayerNetInt( "activeTitanLoadoutIndex", loadoutIndex )
+ }
+
+ void function PROTO_DisplayTitanLoadouts( entity player, entity titan, TitanLoadoutDef loadout )
+ {
+ entity soul = titan.GetTitanSoul()
+ if ( soul.e.embarkCount > 0 )
+ return
+
+ if ( loadout.primary != "" )
+ PROTO_PlayLoadoutNotification( loadout.primary, player )
+ if ( loadout.ordnance != "" )
+ PROTO_PlayLoadoutNotification( loadout.ordnance, player )
+ if ( loadout.special != "" )
+ PROTO_PlayLoadoutNotification( loadout.special, player )
+ if ( loadout.passive1 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive1, player )
+ if ( loadout.passive2 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive2, player )
+ if ( loadout.passive3 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive3, player )
+ if ( loadout.passive4 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive4, player )
+ if ( loadout.passive5 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive5, player )
+ if ( loadout.passive6 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive6, player )
+ }
+
+ void function PROTO_DisplayPilotLoadouts( entity player, PilotLoadoutDef loadout )
+ {
+ if ( loadout.primary != "" )
+ PROTO_PlayLoadoutNotification( loadout.primary, player )
+ if ( loadout.secondary != "" )
+ PROTO_PlayLoadoutNotification( loadout.secondary, player )
+ if ( loadout.ordnance != "" )
+ PROTO_PlayLoadoutNotification( loadout.ordnance, player )
+ if ( loadout.special != "" )
+ PROTO_PlayLoadoutNotification( loadout.special, player )
+ if ( loadout.passive1 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive1, player )
+ if ( loadout.passive2 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive2, player )
+ }
+#endif //SERVER
+
+#if !UI
+ PilotLoadoutDef function GetActivePilotLoadout( entity player )
+ {
+ PilotLoadoutDef loadout
+ loadout.name = string( player.GetPersistentVar( "activePilotLoadout.name" ) )
+ loadout.suit = string( player.GetPersistentVar( "activePilotLoadout.suit" ) )
+ loadout.race = string( player.GetPersistentVar( "activePilotLoadout.race" ) )
+ loadout.execution = string( player.GetPersistentVar( "activePilotLoadout.execution" ) )
+ loadout.primary = string( player.GetPersistentVar( "activePilotLoadout.primary" ) )
+ loadout.primaryAttachment = string( player.GetPersistentVar( "activePilotLoadout.primaryAttachment" ) )
+ loadout.primaryMod1 = string( player.GetPersistentVar( "activePilotLoadout.primaryMod1" ) )
+ loadout.primaryMod2 = string( player.GetPersistentVar( "activePilotLoadout.primaryMod2" ) )
+ loadout.primaryMod3 = string( player.GetPersistentVar( "activePilotLoadout.primaryMod3" ) )
+ loadout.secondary = string( player.GetPersistentVar( "activePilotLoadout.secondary" ) )
+ loadout.secondaryMod1 = string( player.GetPersistentVar( "activePilotLoadout.secondaryMod1" ) )
+ loadout.secondaryMod2 = string( player.GetPersistentVar( "activePilotLoadout.secondaryMod2" ) )
+ loadout.secondaryMod3 = string( player.GetPersistentVar( "activePilotLoadout.secondaryMod3" ) )
+ loadout.weapon3 = string( player.GetPersistentVar( "activePilotLoadout.weapon3" ) )
+ loadout.weapon3Mod1 = string( player.GetPersistentVar( "activePilotLoadout.weapon3Mod1" ) )
+ loadout.weapon3Mod2 = string( player.GetPersistentVar( "activePilotLoadout.weapon3Mod2" ) )
+ loadout.weapon3Mod3 = string( player.GetPersistentVar( "activePilotLoadout.weapon3Mod3" ) )
+ loadout.ordnance = string( player.GetPersistentVar( "activePilotLoadout.ordnance" ) )
+ loadout.passive1 = string( player.GetPersistentVar( "activePilotLoadout.passive1" ) )
+ loadout.passive2 = string( player.GetPersistentVar( "activePilotLoadout.passive2" ) )
+ loadout.skinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.skinIndex" )
+ loadout.camoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.camoIndex" )
+ loadout.primarySkinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.primarySkinIndex" )
+ loadout.primaryCamoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.primaryCamoIndex" )
+ loadout.secondarySkinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.secondarySkinIndex" )
+ loadout.secondaryCamoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.secondaryCamoIndex" )
+ loadout.weapon3SkinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.weapon3SkinIndex" )
+ loadout.weapon3CamoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.weapon3CamoIndex" )
+
+ UpdateDerivedPilotLoadoutData( loadout )
+
+ return loadout
+ }
+
+ TitanLoadoutDef function GetActiveTitanLoadout( entity player )
+ {
+ TitanLoadoutDef loadout
+ loadout.name = string( player.GetPersistentVar( "activeTitanLoadout.name" ) )
+ loadout.titanClass = string( player.GetPersistentVar( "activeTitanLoadout.titanClass" ) )
+ loadout.primaryMod = string( player.GetPersistentVar( "activeTitanLoadout.primaryMod" ) )
+ loadout.special = string( player.GetPersistentVar( "activeTitanLoadout.special" ) )
+ loadout.antirodeo = string( player.GetPersistentVar( "activeTitanLoadout.antirodeo" ) )
+ loadout.passive1 = string( player.GetPersistentVar( "activeTitanLoadout.passive1" ) )
+ loadout.passive2 = string( player.GetPersistentVar( "activeTitanLoadout.passive2" ) )
+ loadout.passive3 = string( player.GetPersistentVar( "activeTitanLoadout.passive3" ) )
+ loadout.passive4 = string( player.GetPersistentVar( "activeTitanLoadout.passive4" ) )
+ loadout.passive5 = string( player.GetPersistentVar( "activeTitanLoadout.passive5" ) )
+ loadout.passive6 = string( player.GetPersistentVar( "activeTitanLoadout.passive6" ) )
+ loadout.titanExecution = string( player.GetPersistentVar( "activeTitanLoadout.titanExecution" ) )
+ loadout.skinIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.skinIndex" )
+ loadout.camoIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.camoIndex" )
+ loadout.decalIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.decalIndex" )
+ loadout.primarySkinIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primarySkinIndex" )
+ loadout.primaryCamoIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primaryCamoIndex" )
+ loadout.isPrime = string( player.GetPersistentVar( "activeTitanLoadout.isPrime" ) )
+ loadout.primeSkinIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primeSkinIndex" )
+ loadout.primeCamoIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primeCamoIndex" )
+ loadout.primeDecalIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primeDecalIndex" )
+ loadout.showArmBadge = player.GetPersistentVarAsInt( "activeTitanLoadout.showArmBadge" )
+
+ UpdateDerivedTitanLoadoutData( loadout )
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout, player )
+
+ //int loadoutIndex = GetActiveTitanLoadoutIndex( player )
+
+ //TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
+ //OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout )
+
+ return loadout
+ }
+
+ // When possible use GetActivePilotLoadout() instead
+ int function GetActivePilotLoadoutIndex( entity player )
+ {
+ #if SERVER
+ return player.p.activePilotLoadoutIndex
+ #else
+ return player.GetPlayerNetInt( "activePilotLoadoutIndex" )
+ #endif
+ }
+
+ int function GetActiveTitanLoadoutIndex( entity player )
+ {
+ //printt( "<<<<<<<<<<<<< GetActiveTitanLoadoutIndex() with index:", player.p.activeTitanLoadoutIndex )
+ #if SERVER
+ return player.p.activeTitanLoadoutIndex
+ #else
+ return player.GetPlayerNetInt( "activeTitanLoadoutIndex" )
+ #endif
+ }
+#endif
+
+// Added doOverrideCallback as a temporary measure to work around an issue the callback override added here is causing
+// This is called in PopulateDefaultPilotLoadouts() which sets the data GetDefaultPilotLoadout() returns
+// GetDefaultPilotLoadout() should never be returning overridden loadouts because it is used in many places to correct or reset persistent loadout data
+void function UpdateDerivedPilotLoadoutData( PilotLoadoutDef loadout, bool doOverrideCallback = true )
+{
+ loadout.setFile = GetSuitAndGenderBasedSetFile( loadout.suit, loadout.race )
+ loadout.special = GetSuitBasedTactical( loadout.suit )
+ loadout.primaryAttachments = [ loadout.primaryAttachment ]
+ loadout.primaryMods = [ loadout.primaryMod1, loadout.primaryMod2, loadout.primaryMod3 ]
+ loadout.secondaryMods = [ loadout.secondaryMod1, loadout.secondaryMod2, loadout.secondaryMod3 ]
+ loadout.weapon3Mods = [ loadout.weapon3Mod1, loadout.weapon3Mod2, loadout.weapon3Mod3 ]
+ loadout.setFileMods = GetSetFileModsForSettingType( "pilot", [ loadout.passive1, loadout.passive2 ] )
+
+ #if SERVER
+ if ( doOverrideCallback )
+ {
+ foreach ( callbackFunc in svGlobal.onUpdateDerivedPilotLoadoutCallbacks )
+ callbackFunc( loadout )
+ }
+ #endif
+}
+
+bool function TitanClassHasPrimeTitan( string titanClass )
+{
+ string nonPrimeSetFile = GetSetFileForTitanClassAndPrimeStatus( titanClass, false )
+ string primeSetFile = GetPrimeTitanSetFileFromNonPrimeSetFile( nonPrimeSetFile )
+
+ return primeSetFile != ""
+}
+
+bool function IsTitanLoadoutPrime( TitanLoadoutDef loadout )
+{
+ return loadout.isPrime == "titan_is_prime"
+}
+
+bool function IsTitanClassPrime( entity player, string titanClass )
+{
+ for ( int i = 0; i < NUM_PERSISTENT_TITAN_LOADOUTS; i++ )
+ {
+ TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, i )
+ if ( loadout.titanClass == titanClass )
+ {
+ if ( loadout.isPrime == "titan_is_prime" )
+ return true
+ else
+ return false
+ }
+ }
+
+ unreachable
+}
+
+void function UpdateDerivedTitanLoadoutData( TitanLoadoutDef loadout )
+{
+ bool isTitanLoadoutPrime = IsTitanLoadoutPrime( loadout )
+ loadout.setFile = GetSetFileForTitanClassAndPrimeStatus( loadout.titanClass, IsTitanLoadoutPrime( loadout ) )
+ loadout.primeTitanRef = GetPrimeTitanRefForTitanClass( loadout.titanClass )
+}
+
+void function PrintPilotLoadoutIndex( entity player, int index )
+{
+ PrintPilotLoadout( GetPilotLoadoutFromPersistentData( player, index ) )
+}
+
+void function PrintTitanLoadoutIndex( entity player, int index )
+{
+ PrintTitanLoadout( GetTitanLoadoutFromPersistentData( player, index ) )
+}
+
+void function PrintPilotLoadouts( entity player )
+{
+ for ( int i = 0; i < NUM_PERSISTENT_PILOT_LOADOUTS; i++ )
+ PrintPilotLoadoutIndex( player, i )
+}
+
+void function PrintTitanLoadouts( entity player )
+{
+ for ( int i = 0; i < NUM_PERSISTENT_TITAN_LOADOUTS; i++ )
+ PrintTitanLoadoutIndex( player, i )
+}
+
+void function PrintPilotLoadout( PilotLoadoutDef loadout )
+{
+ printt( "PILOT LOADOUT:" )
+ printt( " PERSISTENT DATA:" )
+ printt( " name \"" + loadout.name + "\"" )
+ printt( " suit \"" + loadout.suit + "\"" )
+ printt( " race \"" + loadout.race + "\"" )
+ printt( " execution \"" + loadout.execution + "\"" )
+ printt( " primary \"" + loadout.primary + "\"" )
+ printt( " primaryAttachment \"" + loadout.primaryAttachment + "\"" )
+ printt( " primaryMod1 \"" + loadout.primaryMod1 + "\"" )
+ printt( " primaryMod2 \"" + loadout.primaryMod2 + "\"" )
+ printt( " primaryMod3 \"" + loadout.primaryMod3 + "\"" )
+ printt( " primarySkinIndex " + loadout.primarySkinIndex )
+ printt( " primaryCamoIndex " + loadout.primaryCamoIndex )
+ printt( " secondary \"" + loadout.secondary + "\"" )
+ printt( " secondaryMod1 \"" + loadout.secondaryMod1 + "\"" )
+ printt( " secondaryMod2 \"" + loadout.secondaryMod2 + "\"" )
+ printt( " secondaryMod3 \"" + loadout.secondaryMod3 + "\"" )
+ printt( " secondarySkinIndex " + loadout.secondarySkinIndex )
+ printt( " secondaryCamoIndex " + loadout.secondaryCamoIndex )
+ printt( " weapon3 \"" + loadout.weapon3 + "\"" )
+ printt( " weapon3Mod1 \"" + loadout.weapon3Mod1 + "\"" )
+ printt( " weapon3Mod2 \"" + loadout.weapon3Mod2 + "\"" )
+ printt( " weapon3Mod3 \"" + loadout.weapon3Mod3 + "\"" )
+ printt( " weapon3SkinIndex " + loadout.weapon3SkinIndex )
+ printt( " weapon3CamoIndex " + loadout.weapon3CamoIndex )
+ printt( " ordnance \"" + loadout.ordnance + "\"" )
+ printt( " special \"" + loadout.special + "\"" )
+ printt( " passive1 \"" + loadout.passive1 + "\"" )
+ printt( " passive2 \"" + loadout.passive2 + "\"" )
+ printt( " skinIndex " + loadout.skinIndex )
+ printt( " camoIndex " + loadout.camoIndex )
+ printt( " DERIVED DATA:" )
+ printt( " setFile \"" + loadout.setFile + "\"" )
+ print( " setFileMods " )
+ PrintStringArray( loadout.setFileMods )
+ printt( " melee \"" + loadout.melee + "\"" )
+ print( " meleeMods " )
+ PrintStringArray( loadout.meleeMods )
+ print( " primaryAttachments " )
+ PrintStringArray( loadout.primaryAttachments )
+ print( " primaryMods " )
+ PrintStringArray( loadout.primaryMods )
+ print( " secondaryMods " )
+ PrintStringArray( loadout.secondaryMods )
+ print( " weapon3Mods " )
+ PrintStringArray( loadout.weapon3Mods )
+ print( " specialMods " )
+ PrintStringArray( loadout.specialMods )
+ print( " ordnanceMods " )
+ PrintStringArray( loadout.ordnanceMods )
+}
+
+void function PrintTitanLoadout( TitanLoadoutDef loadout )
+{
+ printt( "TITAN LOADOUT:" )
+ printt( " PERSISTENT DATA:" )
+ printt( " name \"" + loadout.name + "\"" )
+ printt( " titanClass \"" + loadout.titanClass + "\"" )
+ printt( " setFile \"" + loadout.setFile + "\"" )
+ printt( " primeTitanRef \"" + loadout.primeTitanRef + "\"" )
+ printt( " primaryMod \"" + loadout.primaryMod + "\"" )
+ printt( " special \"" + loadout.special + "\"" )
+ printt( " antirodeo \"" + loadout.antirodeo + "\"" )
+ printt( " passive1 \"" + loadout.passive1 + "\"" )
+ printt( " passive2 \"" + loadout.passive2 + "\"" )
+ printt( " passive3 \"" + loadout.passive3 + "\"" )
+ printt( " passive4 \"" + loadout.passive4 + "\"" )
+ printt( " passive5 \"" + loadout.passive5 + "\"" )
+ printt( " passive6 \"" + loadout.passive6 + "\"" )
+ printt( " voice \"" + loadout.voice + "\"" )
+ printt( " skinIndex " + loadout.skinIndex )
+ printt( " camoIndex " + loadout.camoIndex )
+ printt( " decalIndex " + loadout.decalIndex )
+ printt( " primarySkinIndex " + loadout.primarySkinIndex )
+ printt( " primaryCamoIndex " + loadout.primaryCamoIndex )
+ printt( " isPrime \"" + loadout.isPrime + "\"" )
+ printt( " primeSkinIndex " + loadout.primeSkinIndex )
+ printt( " primeCamoIndex " + loadout.primeCamoIndex )
+ printt( " primeDecalIndex " + loadout.primeDecalIndex )
+ printt( " DERIVED DATA:" )
+ print( " setFileMods " )
+ PrintStringArray( loadout.setFileMods )
+ printt( " melee \"" + loadout.melee + "\"" )
+ printt( " coreAbility \"" + loadout.coreAbility + "\"" )
+ printt( " primary \"" + loadout.primary + "\"" )
+ printt( " primaryAttachment \"" + loadout.primaryAttachment + "\"" )
+ print( " primaryMods " )
+ PrintStringArray( loadout.primaryMods )
+ printt( " ordnance \"" + loadout.ordnance + "\"" )
+ print( " ordnanceMods " )
+ PrintStringArray( loadout.ordnanceMods )
+ print( " specialMods " )
+ PrintStringArray( loadout.specialMods )
+ print( " antirodeoMods " )
+ PrintStringArray( loadout.antirodeoMods )
+}
+
+void function PrintStringArray( array<string> stringArray )
+{
+ if ( stringArray.len() == 0 )
+ {
+ print( "[]" )
+ }
+ else
+ {
+ for ( int i = 0; i < stringArray.len(); i++ )
+ {
+ if ( i == 0 )
+ print( "[ " )
+
+ print( "\"" + stringArray[i] + "\"" )
+
+ if ( i+1 < stringArray.len() )
+ print( ", " )
+ else
+ print( " ]" )
+ }
+ }
+
+ print( "\n" )
+}
+
+string function GetSkinPropertyName( string camoPropertyName )
+{
+ string skinPropertyName
+
+ switch ( camoPropertyName )
+ {
+ case "camoIndex":
+ skinPropertyName = "skinIndex"
+ break
+
+ case "primeCamoIndex":
+ skinPropertyName = "primeSkinIndex"
+ break
+
+ case "primaryCamoIndex":
+ skinPropertyName = "primarySkinIndex"
+ break
+
+ case "secondaryCamoIndex":
+ skinPropertyName = "secondarySkinIndex"
+ break
+
+ case "weapon3CamoIndex":
+ skinPropertyName = "weapon3SkinIndex"
+ break
+
+ default:
+ Assert( false, "Unknown camoPropertyName: " + camoPropertyName )
+ break
+ }
+
+ return skinPropertyName
+}
+
+int function GetSkinIndexForCamo( string modelType, string camoPropertyName, int camoIndex )
+{
+ Assert( modelType == "pilot" || modelType == "titan" )
+ Assert( camoPropertyName == "camoIndex" || camoPropertyName == "primeCamoIndex" || camoPropertyName == "primaryCamoIndex" || camoPropertyName == "secondaryCamoIndex" || camoPropertyName == "weapon3CamoIndex" )
+
+ int skinIndex = SKIN_INDEX_BASE
+
+ if ( camoIndex > 0 )
+ {
+ if ( camoPropertyName == "camoIndex" || camoPropertyName == "primeCamoIndex" )
+ {
+ if ( modelType == "pilot" )
+ skinIndex = PILOT_SKIN_INDEX_CAMO
+ else
+ skinIndex = TITAN_SKIN_INDEX_CAMO
+ }
+ else if ( camoPropertyName == "primaryCamoIndex" || camoPropertyName == "secondaryCamoIndex" || camoPropertyName == "weapon3CamoIndex" )
+ {
+ skinIndex = WEAPON_SKIN_INDEX_CAMO
+ }
+ }
+
+ return skinIndex
+}
+
+#if SERVER
+void function UpdateProScreen( entity player, entity weapon )
+{
+ int proScreenKills = WeaponGetProScreenKills( player, weapon.GetWeaponClassName() )
+ int previousProScreenKills = WeaponGetPreviousProScreenKills( player, weapon.GetWeaponClassName() )
+ weapon.SetProScreenIntValForIndex( PRO_SCREEN_INT_LIFETIME_KILLS, proScreenKills )
+ weapon.SetProScreenIntValForIndex( PRO_SCREEN_INT_MATCH_KILLS, proScreenKills - previousProScreenKills )
+}
+#endif
+
+bool function IsValidPilotLoadoutIndex( int loadoutIndex )
+{
+ if ( loadoutIndex < 0 )
+ return false
+
+ if( loadoutIndex >= NUM_PERSISTENT_PILOT_LOADOUTS )
+ return false
+
+ return true
+}
+
+bool function IsValidTitanLoadoutIndex( int loadoutIndex )
+{
+ if ( loadoutIndex < 0 )
+ return false
+
+ if( loadoutIndex >= NUM_PERSISTENT_TITAN_LOADOUTS )
+ return false
+
+ return true
+}
+
+bool function HasPrimeToMatchExecutionType( entity player, int itemType )
+{
+ if ( DevEverythingUnlocked() )
+ return true
+
+ switch( itemType )
+ {
+ case eItemTypes.TITAN_RONIN_EXECUTION:
+ return !IsItemLocked( player, "ronin_prime" )
+ case eItemTypes.TITAN_NORTHSTAR_EXECUTION:
+ return !IsItemLocked( player, "northstar_prime" )
+ case eItemTypes.TITAN_ION_EXECUTION:
+ return !IsItemLocked( player, "ion_prime" )
+ case eItemTypes.TITAN_TONE_EXECUTION:
+ return !IsItemLocked( player, "tone_prime" )
+ case eItemTypes.TITAN_SCORCH_EXECUTION:
+ return !IsItemLocked( player, "scorch_prime" )
+ case eItemTypes.TITAN_LEGION_EXECUTION:
+ return !IsItemLocked( player, "legion_prime" )
+ case eItemTypes.TITAN_VANGUARD_EXECUTION:
+ return false
+
+ default:
+ unreachable
+ }
+ unreachable
+}
+
+bool function IsTitanLoadoutAvailable( entity player, string titanClass )
+{
+ int titanClassLockState = player.GetPersistentVarAsInt( "titanClassLockState[" + titanClass + "]" )
+ return (titanClassLockState == TITAN_CLASS_LOCK_STATE_AVAILABLE || titanClassLockState == TITAN_CLASS_LOCK_STATE_LEVELRECOMMENDED)
+}
+
+int function GetTitanLoadAvailableState( entity player, string titanClass )
+{
+ return player.GetPersistentVarAsInt( "titanClassLockState[" + titanClass + "]" )
+}
+
+#if UI || CLIENT
+void function SwapSecondaryAndWeapon3LoadoutData( entity player, int loadoutIndex )
+{
+ // Keep CLIENT matching UI
+ #if UI
+ RunClientScript( "SwapSecondaryAndWeapon3LoadoutData", player, loadoutIndex )
+ #endif // UI
+
+ string loadoutType = "pilot"
+
+ PilotLoadoutDef loadout
+ loadout.secondary = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondary
+ loadout.secondaryMod1 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryMod1
+ loadout.secondaryMod2 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryMod2
+ loadout.secondaryMod3 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryMod3
+ loadout.secondarySkinIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondarySkinIndex
+ loadout.secondaryCamoIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryCamoIndex
+
+ loadout.weapon3 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3
+ loadout.weapon3Mod1 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3Mod1
+ loadout.weapon3Mod2 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3Mod2
+ loadout.weapon3Mod3 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3Mod3
+ loadout.weapon3SkinIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3SkinIndex
+ loadout.weapon3CamoIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3CamoIndex
+
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondary", loadout.weapon3 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryMod1", loadout.weapon3Mod1 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryMod2", loadout.weapon3Mod2 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryMod3", loadout.weapon3Mod3 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondarySkinIndex", string( loadout.weapon3SkinIndex ) )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryCamoIndex", string( loadout.weapon3CamoIndex ) )
+
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3", loadout.secondary )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3Mod1", loadout.secondaryMod1 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3Mod2", loadout.secondaryMod2 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3Mod3", loadout.secondaryMod3 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3SkinIndex", string( loadout.secondarySkinIndex ) )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3CamoIndex", string( loadout.secondaryCamoIndex ) )
+
+ #if UI
+ ClientCommand( "SwapSecondaryAndWeapon3PersistentLoadoutData " + loadoutIndex )
+ #endif // UI
+}
+#endif // UI || CLIENT