From a352fbb877aabdaf9b124eee47fb78f2ac0007a6 Mon Sep 17 00:00:00 2001 From: Connie Price Date: Tue, 4 Jan 2022 19:13:56 +0000 Subject: Big Boost/Earn Meter Fixes! * Only set the boost from persistence once, allowing for the boost to be changed mid game by other scripts. * Added `PlayerEarnMeter_SetBoostByRef(string boostRef)` to simplify setting boosts. * Boosts now correctly disappear after being reward to the player. * Client-side reward icon will now update when the boost is changed. * Titan cores will now pull the HUD icon from the core, instead of the titan chassis. Allowing this to be handled better by other scripts. * Player boost inventory implemented! Now players will be able to store multiple boosts like vanilla. * Boost inventory limits, all vanilla boost limits are respected, and `BurnMeter_SetBoostLimit( string burnRef, int limit )` was added to accommodate this. * Fixed the unused nuke titan boost code so that if anyone decides to use it it's not horribly error prone. --- .../scripts/vscripts/earn_meter/cl_earn_meter.gnut | 573 +++++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut (limited to 'Northstar.Custom/mod/scripts/vscripts/earn_meter') diff --git a/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut b/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut new file mode 100644 index 000000000..5de6d956a --- /dev/null +++ b/Northstar.Custom/mod/scripts/vscripts/earn_meter/cl_earn_meter.gnut @@ -0,0 +1,573 @@ +global function Cl_EarnMeter_Init +global function Cl_EarnMeter_RegisterNetworkFunctions + +global function Cl_EarnMeter_ShowRewardHint +global function Cl_EarnMeter_SetLastHintTime + +global function Cl_EarnMeter_UpdateHint + +global function ServerCallback_EarnMeterAwarded + +#if SP +global function Cl_SP_UpdateCoreIcon +#endif + +global function EarnMeter_Update + +const float FILL_ANIM_DURATION = 0.7 + +struct EarnChangeData +{ + float startValue = 0.0 + float endValue = 0.0 + float updateTime = 0.0 +} + +struct +{ + EarnChangeData lastEarnedData + EarnChangeData lastOwnedData + + var earnMeterRui + var rewardRui + + var meterHintRui + + float lastHintTime +} file + +void function Cl_EarnMeter_Init() +{ + if ( !EARNMETER_ENABLED ) + return + + AddCallback_OnClientScriptInit( Cl_EarnMeter_AddClient ) + AddCallback_KillReplayStarted( KillReplayStarted ) + AddCallback_KillReplayEnded( KillReplayEnded ) + AddFirstPersonSpectateStartedCallback( FirstPersonSpectate_UpdateEarnMeter ) + AddFirstPersonSpectateEndedCallback( FirstPersonSpectate_UpdateEarnMeter ) + AddCallback_OnPlayerLifeStateChanged( Callback_PlayerLifestateChanged ) + AddCallback_OnPetTitanModeChanged( Callback_PetTitanModeChanged ) + AddCallback_OnPetTitanChanged( Callback_PetTitanChanged ) + + RegisterSignal( "Callback_PetTitanChanged" ) + + var rui = CreateCockpitRui( $"ui/earn_meter.rpak" ) + RuiSetFloat( rui, "fillAnimDuration", FILL_ANIM_DURATION ) + RuiSetGameTime( rui, "lastEarnChangeTime", RUI_BADGAMETIME ) + RuiSetGameTime( rui, "lastOwnChangeTime", RUI_BADGAMETIME ) + RuiSetFloat( rui, "earnedStartFrac", 0.0 ) + RuiSetFloat( rui, "ownedStartFrac", 0.0 ) + + file.earnMeterRui = rui + + file.meterHintRui = CreateCockpitRui( $"ui/meter_hint.rpak" ) + + { + //var rui = CreateCockpitRui( $"ui/reward_hud.rpak" ) + //file.rewardRui = rui + } +} + + +void function Cl_EarnMeter_RegisterNetworkFunctions() +{ + RegisterNetworkedVariableChangeCallback_float( EARNMETER_EARNEDFRAC, EarnedFracUpdate ) + RegisterNetworkedVariableChangeCallback_float( EARNMETER_OWNEDFRAC, OwnedFracUpdate ) + + RegisterNetworkedVariableChangeCallback_float( "coreAvailableFrac", CoreFracUpdate ) + + RegisterNetworkedVariableChangeCallback_int( EARNMETER_GOALID, GoalIdUpdate ) + RegisterNetworkedVariableChangeCallback_int( EARNMETER_REWARDID, RewardIdUpdate ) + RegisterNetworkedVariableChangeCallback_int( EARNMETER_MODE, ModeUpdate ) + RegisterNetworkedVariableChangeCallback_int( "rewardState", RewardStateUpdate ) + RegisterNetworkedVariableChangeCallback_int( "goalState", GoalStateUpdate ) +} + + +void function Cl_EarnMeter_AddClient( entity player ) +{ + EarnMeter_Update() + thread EarnMeter_Ping() +} + + +void function EarnedFracUpdate( entity player, float oldValue, float newValue, bool actuallyChanged ) +{ + if ( !actuallyChanged ) + return + + float currentValue = Cl_EarnMeter_GetCurrentMeterValue( file.lastEarnedData ) + RuiSetGameTime( file.earnMeterRui, "lastEarnChangeTime", Time() ) + RuiSetFloat( file.earnMeterRui, "earnedStartFrac", currentValue ) + + file.lastEarnedData.startValue = oldValue + file.lastEarnedData.endValue = newValue + file.lastEarnedData.updateTime = Time() +} + + +void function OwnedFracUpdate( entity player, float oldValue, float newValue, bool actuallyChanged ) +{ + if ( !actuallyChanged ) + return + + float currentValue = Cl_EarnMeter_GetCurrentMeterValue( file.lastOwnedData ) + RuiSetGameTime( file.earnMeterRui, "lastOwnChangeTime", Time() ) + RuiSetFloat( file.earnMeterRui, "ownedStartFrac", currentValue ) + + file.lastOwnedData.startValue = oldValue + file.lastOwnedData.endValue = newValue + file.lastOwnedData.updateTime = Time() +} + + +void function CoreFracUpdate( entity player, float oldValue, float newValue, bool actuallyChanged ) +{ + // net var lives on soul, which is global to all players + if ( player != GetLocalViewPlayer() ) + return + + if ( !actuallyChanged ) + return + + float currentValue = Cl_EarnMeter_GetCurrentMeterValue( file.lastOwnedData ) + RuiSetGameTime( file.earnMeterRui, "lastOwnChangeTime", Time() ) + RuiSetFloat( file.earnMeterRui, "ownedStartFrac", currentValue ) + + file.lastOwnedData.startValue = oldValue + file.lastOwnedData.endValue = newValue + file.lastOwnedData.updateTime = Time() +} + + +float function Cl_EarnMeter_GetCurrentMeterValue( EarnChangeData earnData ) +{ + float elapsedTime = Time() - earnData.updateTime + float delta = earnData.endValue - earnData.startValue + return earnData.endValue - (delta * EaseIn( GraphCapped( elapsedTime, 0.0, FILL_ANIM_DURATION, 1.0, 0.0 ) ) ) +} + + +void function RewardStateUpdate( entity player, int oldValue, int newValue, bool actuallyChanged ) +{ + if ( player != GetLocalViewPlayer() ) + return + + //if ( newValue == eRewardState.AVAILABLE ) + // RuiSetBool( file.rewardRui, "isVisible", true ) + //else + // RuiSetBool( file.rewardRui, "isVisible", false ) + + file.lastHintTime = 0 + if ( newValue == eRewardState.AVAILABLE ) + Cl_EarnMeter_ShowRewardHint() + + RuiSetInt( file.earnMeterRui, "rewardState", newValue ) +} + + +void function GoalStateUpdate( entity player, int oldValue, int newValue, bool actuallyChanged ) +{ + if ( player != GetLocalViewPlayer() ) + return + + file.lastHintTime = 0 + if ( newValue == eRewardState.AVAILABLE ) + Cl_EarnMeter_ShowRewardHint() + + RuiSetInt( file.earnMeterRui, "goalState", newValue ) +} + + +void function ModeUpdate( entity player, int oldValue, int newValue, bool actuallyChanged ) +{ + file.lastOwnedData.startValue = 0.0 + file.lastOwnedData.endValue = 0.0 + file.lastOwnedData.updateTime = 0.0 + + EarnMeter_Update() +} + +void function GoalIdUpdate( entity player, int oldValue, int newValue, bool actuallyChanged ) +{ + EarnMeter_Update() +} + +void function RewardIdUpdate( entity player, int oldValue, int newValue, bool actuallyChanged ) +{ + EarnMeter_Update() +} + +void function KillReplayStarted() +{ + EarnMeter_Update() +} + + +void function KillReplayEnded() +{ + EarnMeter_Update() +} + +void function FirstPersonSpectate_UpdateEarnMeter( entity clientPlayer, entity observerTarget ) +{ + EarnMeter_Update() +} + +void function Cl_EarnMeter_ShowRewardHint() +{ + entity player = GetLocalViewPlayer() + + if ( player == null ) + return + + if ( PlayerEarnMeter_GetMode( player ) == eEarnMeterMode.DEFAULT || PlayerEarnMeter_GetMode( player ) == eEarnMeterMode.CORE ) + { + if ( Time() - file.lastHintTime > 30.0 ) + { + RuiSetGameTime( file.earnMeterRui, "lastHintTime", Time() ) + file.lastHintTime = Time() + #if MP + if ( PlayerEarnMeter_GetMode( player ) == eEarnMeterMode.DEFAULT ) + { + TitanReadyMessage( 0.0, true ) + } + #endif + } + } +} + + +void function EarnMeter_Update() +{ + entity player = GetLocalViewPlayer() + if ( player == null ) + return + + if ( file.earnMeterRui == null ) + return + + switch ( PlayerEarnMeter_GetMode( player ) ) + { + case eEarnMeterMode.DISABLED: + break + + case eEarnMeterMode.DEFAULT: + EarnObject earnReward = PlayerEarnMeter_GetReward( player ) + EarnObject earnGoal = PlayerEarnMeter_GetGoal( player ) + + //RuiTrackFloat( file.meterHintRui, "earnedFrac", player, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( EARNMETER_EARNEDFRAC ) ) + //RuiTrackFloat( file.meterHintRui, "ownedFrac", player, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( EARNMETER_OWNEDFRAC ) ) + + RuiTrackFloat( file.earnMeterRui, "earnedFrac", player, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( EARNMETER_EARNEDFRAC ) ) + RuiTrackFloat( file.earnMeterRui, "ownedFrac", player, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( EARNMETER_OWNEDFRAC ) ) + RuiTrackFloat( file.earnMeterRui, "rewardFrac", player, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( EARNMETER_REWARDFRAC ) ) + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", earnGoal.buildingImage ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", earnGoal.readyImage ) + RuiSetInt( file.earnMeterRui, "goalState", player.GetPlayerNetInt( "goalState" ) ) + RuiSetInt( file.earnMeterRui, "goalSubState", 0 ) + + RuiSetImage( file.earnMeterRui, "rewardBuildingIcon", earnReward.buildingImage ) + RuiSetImage( file.earnMeterRui, "rewardReadyIcon", earnReward.readyImage ) + RuiSetString( file.earnMeterRui, "rewardString", earnReward.localizedName ) + RuiSetInt( file.earnMeterRui, "rewardState", player.GetPlayerNetInt( "rewardState" ) ) + break + + case eEarnMeterMode.CORE: + if ( !IsValid( player.GetTitanSoul() ) ) + break + + entity soul = player.GetTitanSoul() + entity core = player.GetOffhandWeapons()[3] + string coreName = core.GetWeaponClassName() + array coreMods = core.GetMods() + + #if SP + RuiTrackFloat( file.earnMeterRui, "earnedFrac", soul, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( "coreAvailableFrac" ) ) + RuiTrackFloat( file.earnMeterRui, "ownedFrac", soul, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( "coreAvailableFrac" ) ) + RuiSetFloat( file.earnMeterRui, "rewardFrac", 0.0 ) + RuiSetString( file.earnMeterRui, "rewardString", "" ) + + Cl_SP_UpdateCoreIcon( -1 ) + + RuiSetInt( file.earnMeterRui, "rewardState", eRewardState.DISABLED ) + RuiSetInt( file.earnMeterRui, "goalSubState", 0 ) + #endif + + #if MP + RuiTrackFloat( file.earnMeterRui, "earnedFrac", soul, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( "coreAvailableFrac" ) ) + RuiTrackFloat( file.earnMeterRui, "ownedFrac", soul, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( "coreAvailableFrac" ) ) + RuiTrackFloat( file.earnMeterRui, "rewardFrac", player, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( EARNMETER_REWARDFRAC ) ) + + if ( coreName == "mp_titancore_upgrade" ) + { + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", GetVanguardCoreIcon( player ) ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", GetVanguardCoreIcon( player ) ) + } + else + { + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", GetWeaponInfoFileKeyFieldAsset_WithMods_Global( coreName, coreMods, "hud_icon" ) ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", GetWeaponInfoFileKeyFieldAsset_WithMods_Global( coreName, coreMods, "hud_icon" ) ) + } + + EarnObject earnReward = PlayerEarnMeter_GetReward( player ) + RuiSetImage( file.earnMeterRui, "rewardBuildingIcon", earnReward.buildingImage ) + RuiSetImage( file.earnMeterRui, "rewardReadyIcon", earnReward.readyImage ) + RuiSetString( file.earnMeterRui, "rewardString", earnReward.localizedName ) + RuiSetInt( file.earnMeterRui, "rewardState", player.GetPlayerNetInt( "rewardState" ) ) + + RuiSetInt( file.earnMeterRui, "goalSubState", 0 ) + #endif + break + + case eEarnMeterMode.CORE_ACTIVE: + if ( !IsValid( player.GetTitanSoul() ) ) + break + + entity soul = player.GetTitanSoul() + entity core = player.GetOffhandWeapons()[3] + string coreName = core.GetWeaponClassName() + array coreMods = core.GetMods() + + #if SP + Cl_SP_UpdateCoreIcon( -1 ) + #endif + + #if MP + if ( coreName == "mp_titancore_upgrade" ) + { + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", GetVanguardCoreIcon( player, 1 ) ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", GetVanguardCoreIcon( player, 1 ) ) + } + else + { + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", GetWeaponInfoFileKeyFieldAsset_WithMods_Global( coreName, coreMods, "hud_icon" ) ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", GetWeaponInfoFileKeyFieldAsset_WithMods_Global( coreName, coreMods, "hud_icon" ) ) + } + #endif + + RuiTrackFloat( file.earnMeterRui, "earnedFrac", soul, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( "coreExpireFrac" ) ) + RuiTrackFloat( file.earnMeterRui, "ownedFrac", soul, RUI_TRACK_SCRIPT_NETWORK_VAR, GetNetworkedVariableIndex( "coreExpireFrac" ) ) + RuiSetFloat( file.earnMeterRui, "rewardFrac", 0.0 ) + RuiSetString( file.earnMeterRui, "rewardString", "" ) + RuiSetInt( file.earnMeterRui, "rewardState", eRewardState.DISABLED ) + RuiSetInt( file.earnMeterRui, "goalSubState", 0 ) + + break + + case eEarnMeterMode.PET: + if ( !IsValid( player.GetPetTitan() ) ) + break + + entity titan = player.GetPetTitan() + string settingsName = PlayerSettingsIndexToName( titan.GetTitanSoul().GetPlayerSettingsNum() ) + + RuiSetBool( file.earnMeterRui, "isPetDoomed", titan.GetTitanSoul().IsDoomed() ) + RuiTrackFloat( file.earnMeterRui, "earnedFrac", titan, RUI_TRACK_HEALTH ) + RuiTrackFloat( file.earnMeterRui, "ownedFrac", titan, RUI_TRACK_HEALTH ) + RuiSetFloat( file.earnMeterRui, "rewardFrac", 0.0 ) + RuiSetString( file.earnMeterRui, "rewardString", "" ) + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", Dev_GetPlayerSettingAssetByKeyField_Global( settingsName, "hud_follow_icon" ) ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", Dev_GetPlayerSettingAssetByKeyField_Global( settingsName, "hud_guard_icon" ) ) + RuiSetInt( file.earnMeterRui, "rewardState", eRewardState.DISABLED ) + RuiSetInt( file.earnMeterRui, "goalSubState", player.GetPetTitanMode() ) + break + } + + RuiSetInt( file.earnMeterRui, "meterMode", PlayerEarnMeter_GetMode( player ) ) + + Cl_EarnMeter_UpdateHint( player ) +} + +asset function GetVanguardCoreIcon( entity player, int countOffset = 0 ) +{ + Assert( player.IsTitan() ) + + if ( !IsConnected() ) //Persistence isn't available when we disconnect + return $"" + + if ( player != GetLocalClientPlayer() ) //Client Persistence doesn't know about other players. + return Dev_GetPlayerSettingAssetByKeyField_Global( "titan_atlas_vanguard", "core_building_icon" ) + + #if MP + TitanLoadoutDef loadout = GetActiveTitanLoadout( player ) + + entity soul = player.GetTitanSoul() + if ( !IsValid( soul ) ) + return $"" + + int upgradeCount = soul.GetTitanSoulNetInt( "upgradeCount" ) - countOffset + if ( upgradeCount == 0 && loadout.passive4 != "" ) + { + return GetItemImage( loadout.passive4 ) + } + else if ( upgradeCount == 1 && loadout.passive5 != "" ) + { + return GetItemImage( loadout.passive5 ) + } + else if ( upgradeCount == 2 && loadout.passive6 != "" ) + { + return GetItemImage( loadout.passive6 ) + } + else + { + return Dev_GetPlayerSettingAssetByKeyField_Global( "titan_atlas_vanguard", "core_building_icon" ) + } + #endif + + return $"" +} + +void function Cl_EarnMeter_UpdateHint( entity player ) +{ + if ( GetConVarInt( "hud_setting_showButtonHints" ) == 0 ) + RuiSetString( file.earnMeterRui, "buttonHintText", "" ) + else + RuiSetString( file.earnMeterRui, "buttonHintText", "%ability 1%" ) +} + + +void function Callback_PetTitanModeChanged( entity player ) +{ + EarnMeter_Update() +} + +void function Callback_PetTitanChanged( entity player ) +{ + if ( IsValid( player.GetPetTitan() ) ) + thread PetTitan_MeterUpdateThink( player, player.GetPetTitan() ) + + EarnMeter_Update() +} + + +void function PetTitan_MeterUpdateThink( entity player, entity titan ) +{ + titan.EndSignal( "OnDeath" ) + titan.EndSignal( "OnDestroy" ) + + player.Signal( "Callback_PetTitanChanged" ) + player.EndSignal( "Callback_PetTitanChanged" ) + + titan.EnableHealthChangedCallback() + + while ( true ) + { + WaitSignal( titan, "Doomed", "HealthChanged" ) + EarnMeter_Update() + } +} + + +void function Callback_PlayerLifestateChanged( entity player, int oldLifeState, int newLifeState ) +{ + if ( player != GetLocalViewPlayer() ) + return + + if ( newLifeState == LIFE_ALIVE ) + { + EarnMeter_Update() + Cl_EarnMeter_ShowRewardHint() + } +} + +void function EarnMeter_Ping() +{ + while ( true ) + { + entity player = GetLocalClientPlayer() + + switch ( PlayerEarnMeter_GetMode( player ) ) + { + case eEarnMeterMode.DEFAULT: + if ( player.GetPlayerNetInt( "rewardState" ) == eRewardState.AVAILABLE || player.GetPlayerNetInt( "goalState" ) == eRewardState.AVAILABLE ) + Cl_EarnMeter_ShowRewardHint() + break + + case eEarnMeterMode.CORE: + if ( !IsValid( player.GetTitanSoul() ) ) + break + + entity soul = player.GetTitanSoul() + if ( soul.GetTitanSoulNetFloat( "coreAvailableFrac" ) == 1.0 ) + Cl_EarnMeter_ShowRewardHint() + break + + } + + + wait 5.0 + } +} + +void function Cl_EarnMeter_SetLastHintTime( float time ) +{ + file.lastHintTime = time +} + + +#if SP +void function Cl_SP_UpdateCoreIcon( int loadoutIndex ) +{ + #if SP + string primaryWeaponName + + entity player = GetLocalViewPlayer() + if ( player == null ) + return + + if ( !player.IsTitan() ) + return + + if ( loadoutIndex == -1 ) + { + array weapons = player.GetMainWeapons() + + if ( weapons.len() <= 0 ) // JFS + return + + entity primaryWeapon = weapons[0] + primaryWeaponName = primaryWeapon.GetWeaponClassName() + } + else + { + primaryWeaponName = GetSPTitanLoadoutForIndex_PrimaryWeapon( loadoutIndex ) + } + + asset coreBuildingIcon = SP_GetIconForTitanWeapon( primaryWeaponName, "coreBuildingIcon" ) + asset coreReadyIcon = SP_GetIconForTitanWeapon( primaryWeaponName, "coreReadyIcon" ) + + RuiSetImage( file.earnMeterRui, "goalBuildingIcon", coreBuildingIcon ) + RuiSetImage( file.earnMeterRui, "goalReadyIcon", coreReadyIcon ) + #endif +} + + +asset function SP_GetIconForTitanWeapon( string weaponName, string iconName ) +{ + var dataTable = GetDataTable( $"datatable/titan_properties.rpak" ) + int row = GetDataTableRowMatchingStringValue( dataTable, GetDataTableColumnByName( dataTable, "primary" ), weaponName ) + + #if DEV + // Art uses a weapon_cubemap that breaks the Assert below, so just make it a warning in dev + if ( row == -1 ) + { + CodeWarning( "No loadout for weapon " + weaponName ) + return $"" + } + #endif + + Assert( row != -1, "No loadout for weapon " + weaponName ) + + asset icon = GetDataTableAsset( dataTable, row, GetDataTableColumnByName( dataTable, iconName ) ) + return icon +} + +#endif + + +void function ServerCallback_EarnMeterAwarded( float addedEarnedFrac, float addedOwnedFrac ) +{ + printt( "ServerCallback_EarnMeterAwarded", addedEarnedFrac, addedOwnedFrac ) +} \ No newline at end of file -- cgit v1.2.3