拼ui 一些业务逻辑x实现

This commit is contained in:
FloatGaming
2026-03-19 06:15:09 +08:00
parent f5c6f143c0
commit 49e45ac464
1118 changed files with 246518 additions and 5368 deletions
+34 -2
View File
@@ -90,6 +90,8 @@ public class AllyHero_SO : ScriptableObject
[Header("Inspector")]
public int ally_currentEXP;
public int ally_battleDeployCount;
public int ally_finishCount;
public int ally_mvpCount;
[Header("Skills")]
public SkillDefinition[] availableSkills;
@@ -209,7 +211,10 @@ public class AllyHero_SO : ScriptableObject
if (isUnlocked == value) return;
isUnlocked = value;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
if (!Application.isPlaying)
{
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
@@ -218,7 +223,10 @@ public class AllyHero_SO : ScriptableObject
if (amount <= 0) return;
AllyHeroDeployLedger.EnsureInstance().IncrementDeployCount(this, amount);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
if (!Application.isPlaying)
{
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
@@ -231,4 +239,28 @@ public class AllyHero_SO : ScriptableObject
{
AllyHeroDeployLedger.EnsureInstance().SaveNow();
}
public void IncrementFinishCount(int amount = 1)
{
if (amount <= 0) return;
AllyHeroDeployLedger.EnsureInstance().IncrementFinishCount(this, amount);
#if UNITY_EDITOR
if (!Application.isPlaying)
{
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
public void IncrementMvpCount(int amount = 1)
{
if (amount <= 0) return;
AllyHeroDeployLedger.EnsureInstance().IncrementMvpCount(this, amount);
#if UNITY_EDITOR
if (!Application.isPlaying)
{
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
}
+4 -1
View File
@@ -189,7 +189,10 @@ public class Player_SO : ScriptableObject
private void PersistEditorChanges()
{
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
if (!Application.isPlaying)
{
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
}
@@ -4986,7 +4986,7 @@ MonoBehaviour:
showLevel_prefab: {fileID: 775714823646948286, guid: 4c26aec0fc471c24d94487eff6821e14, type: 3}
store_prefab: {fileID: 3878406062860244594, guid: d94caf56bd6dff64fa606eeeb6e06fa9, type: 3}
settings_prefab: {fileID: 4801108313180107556, guid: 8db6dd820b152984980f7fd2124138c3, type: 3}
userInfo_prefab: {fileID: 0}
userInfo_prefab: {fileID: 5155954718781159675, guid: 18f4d2367ef2dff429599f1580fd6bad, type: 3}
email_prefab: {fileID: 3756075665884320709, guid: 41f4ea31b64f18d48aeef21b79930a6c, type: 3}
notice_prefab: {fileID: 8527835049849151044, guid: 06bb6d3a45697374ab4f5ff36356d138, type: 3}
back_navButton: {fileID: 0}
@@ -10,6 +10,11 @@ using Steamworks;
public class btmandtopController : MonoBehaviour, ICancelHandler
{
public static event System.Action<bool> GlobalSettingsVisibilityChanged;
public static bool CurrentSettingsVisible { get; private set; }
public event System.Action<bool> SettingsVisibilityChanged;
[Header("player unique so")]
public Player_SO player_SO;
public Text playerCoins_legacy;
@@ -98,6 +103,7 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
// cached reference to the settings instance to ensure only one exists
private GameObject settingsInstance;
private GameObject userInfoInstance;
private GameObject storeInstance;
private GameObject showLevelInstance;
private GameObject emailInstance;
@@ -106,6 +112,7 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
private Coroutine musicPicFade;
private bool musicPicVisible = true;
private bool navSceneLoading = false;
private bool lastSettingsVisibilityState;
private readonly Dictionary<string, int> guideIndexByScene = new Dictionary<string, int>();
private string currentGuideScene = string.Empty;
@@ -137,7 +144,7 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
}
}
instantiateSettings = () => ToggleSettingsPrefab();
instantiateUserInfo = () => { gNotice.recommendation.display("功能即将上线,敬请期待"); };
instantiateUserInfo = () => ToggleUserInfoPrefab();
instantiateStore = () => ShowStorePrefab();
instantiateShowLevel = () => { gNotice.error.display("功能即将下线,禁止访问"); };
// instantiateShowLevel = () => ShowLevelPrefab();
@@ -202,6 +209,7 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
{
settingsInstance = Instantiate(settings_prefab, putPrefabsHere.transform);
EnsureSettingsEnterAnimator(settingsInstance);
EnsureSettingsLastSibling();
settingsInstance.SetActive(false);
// try to set canvas camera now as in previous behavior
@@ -225,10 +233,39 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
{
// ensure it's parented correctly under putPrefabsHere
settingsInstance.transform.SetParent(putPrefabsHere.transform, false);
EnsureSettingsLastSibling();
EnsureSettingsEnterAnimator(settingsInstance);
settingsInstance.SetActive(false);
}
}
if (userInfo_prefab != null && putPrefabsHere != null)
{
if (userInfoInstance == null)
{
var existing = GameObject.Find(userInfo_prefab.name + "(Clone)");
if (existing != null)
{
userInfoInstance = existing;
}
}
if (userInfoInstance == null)
{
userInfoInstance = Instantiate(userInfo_prefab, putPrefabsHere.transform);
TryAssignCanvasCamera(userInfoInstance);
PlacePanelBelowSettings(userInfoInstance);
userInfoInstance.SetActive(false);
}
else
{
userInfoInstance.transform.SetParent(putPrefabsHere.transform, false);
PlacePanelBelowSettings(userInfoInstance);
userInfoInstance.SetActive(false);
}
}
lastSettingsVisibilityState = IsSettingsPanelVisible;
}
@@ -240,6 +277,26 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
}
}
private void Update()
{
if (!IsUiUiSceneActive())
{
if (CurrentSettingsVisible)
{
BroadcastSettingsVisibility(false);
}
return;
}
bool visible = IsSettingsPanelVisible;
if (visible == lastSettingsVisibilityState)
{
return;
}
BroadcastSettingsVisibility(visible);
}
private void ToggleMusicPic()
@@ -581,6 +638,7 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
{
settingsInstance = Instantiate(settings_prefab, putPrefabsHere.transform);
EnsureSettingsEnterAnimator(settingsInstance);
EnsureSettingsLastSibling();
// attempt to set canvas camera
try
@@ -600,43 +658,111 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
catch { }
settingsInstance.SetActive(true);
EnsureSettingsLastSibling();
BroadcastSettingsVisibility(true);
return;
}
EnsureSettingsEnterAnimator(settingsInstance);
settingsInstance.transform.SetParent(putPrefabsHere.transform, false);
EnsureSettingsLastSibling();
// Toggle active state
bool active = settingsInstance.activeSelf;
settingsInstance.SetActive(!active);
if (settingsInstance.activeSelf)
{
EnsureSettingsLastSibling();
}
BroadcastSettingsVisibility(settingsInstance.activeSelf);
}
public bool IsSettingsPanelVisible
{
get { return settingsInstance != null && settingsInstance.activeInHierarchy; }
}
private void ToggleUserInfoPrefab()
{
if (userInfo_prefab == null || putPrefabsHere == null) return;
if (userInfoInstance == null)
{
userInfoInstance = Instantiate(userInfo_prefab, putPrefabsHere.transform);
TryAssignCanvasCamera(userInfoInstance);
PlacePanelBelowSettings(userInfoInstance);
userInfoInstance.SetActive(true);
return;
}
userInfoInstance.transform.SetParent(putPrefabsHere.transform, false);
userInfoInstance.SetActive(!userInfoInstance.activeSelf);
if (userInfoInstance.activeSelf)
{
PlacePanelBelowSettings(userInfoInstance);
}
}
private void BroadcastSettingsVisibility(bool visible)
{
lastSettingsVisibilityState = visible;
if (CurrentSettingsVisible == visible)
{
SettingsVisibilityChanged?.Invoke(visible);
return;
}
CurrentSettingsVisible = visible;
SettingsVisibilityChanged?.Invoke(visible);
GlobalSettingsVisibilityChanged?.Invoke(visible);
}
private bool IsUiUiSceneActive()
{
string sceneName = SceneManager.GetActiveScene().name;
string targetScene = string.IsNullOrWhiteSpace(uiSceneName) ? "UI_UI" : uiSceneName;
return string.Equals(sceneName, targetScene, System.StringComparison.OrdinalIgnoreCase);
}
private void ShowLevelPrefab()
{
ShowPrefab(showLevel_prefab, ref showLevelInstance);
CloseInfoPanels(showLevelInstance);
bool opened = ShowPrefab(showLevel_prefab, ref showLevelInstance);
if (opened)
{
CloseInfoPanels(showLevelInstance);
}
}
private void ShowStorePrefab()
{
ShowPrefab(store_prefab, ref storeInstance);
CloseInfoPanels(storeInstance);
bool opened = ShowPrefab(store_prefab, ref storeInstance);
if (opened)
{
CloseInfoPanels(storeInstance);
}
}
private void ShowEmailPrefab()
{
ShowPrefab(email_prefab, ref emailInstance);
CloseInfoPanels(emailInstance);
bool opened = ShowPrefab(email_prefab, ref emailInstance);
if (opened)
{
CloseInfoPanels(emailInstance);
}
}
private void ShowNoticePrefab()
{
ShowPrefab(notice_prefab, ref noticeInstance);
CloseInfoPanels(noticeInstance);
bool opened = ShowPrefab(notice_prefab, ref noticeInstance);
if (opened)
{
CloseInfoPanels(noticeInstance);
}
}
private void ShowPrefab(GameObject prefab, ref GameObject instance)
private bool ShowPrefab(GameObject prefab, ref GameObject instance)
{
if (prefab == null || putPrefabsHere == null) return;
if (prefab == null || putPrefabsHere == null) return false;
if (instance == null)
{
@@ -652,8 +778,48 @@ public class btmandtopController : MonoBehaviour, ICancelHandler
}
}
instance.SetActive(true);
instance.transform.SetAsLastSibling();
bool shouldOpen = !instance.activeSelf;
instance.SetActive(shouldOpen);
if (!shouldOpen)
{
return false;
}
PlacePanelBelowSettings(instance);
return true;
}
private void EnsureSettingsLastSibling()
{
if (settingsInstance == null || putPrefabsHere == null)
{
return;
}
settingsInstance.transform.SetParent(putPrefabsHere.transform, false);
settingsInstance.transform.SetAsLastSibling();
}
private void PlacePanelBelowSettings(GameObject instance)
{
if (instance == null || putPrefabsHere == null)
{
return;
}
instance.transform.SetParent(putPrefabsHere.transform, false);
if (settingsInstance == null || instance == settingsInstance)
{
instance.transform.SetAsLastSibling();
return;
}
EnsureSettingsLastSibling();
int settingsIndex = settingsInstance.transform.GetSiblingIndex();
int targetIndex = Mathf.Clamp(settingsIndex, 0, putPrefabsHere.transform.childCount - 1);
instance.transform.SetSiblingIndex(targetIndex);
EnsureSettingsLastSibling();
}
private void CloseInfoPanels(GameObject keep)
@@ -11,7 +11,12 @@ public class loadtopandbottomPrefab: MonoBehaviour
{
if (top_and_bottom_Panel != null && gameobject_to_Instantiate_below != null)
{
GameObject instantiated = Instantiate(top_and_bottom_Panel, gameobject_to_Instantiate_below.transform);
GameObject instantiated = FindExistingPanelInstance();
if (instantiated == null)
{
instantiated = Instantiate(top_and_bottom_Panel, gameobject_to_Instantiate_below.transform);
}
Canvas canvas = instantiated.GetComponent<Canvas>();
if (canvas != null)
{
@@ -30,6 +35,34 @@ public class loadtopandbottomPrefab: MonoBehaviour
}
}
GameObject FindExistingPanelInstance()
{
if (gameobject_to_Instantiate_below == null || top_and_bottom_Panel == null)
{
return null;
}
string prefabName = top_and_bottom_Panel.name;
Transform parent = gameobject_to_Instantiate_below.transform;
for (int i = 0; i < parent.childCount; i++)
{
Transform child = parent.GetChild(i);
if (child == null)
{
continue;
}
bool nameMatches = child.name == prefabName || child.name == prefabName + "(Clone)";
bool hasBars = child.Find("TOP") != null || child.Find("BTM") != null;
if (nameMatches && hasBars)
{
return child.gameObject;
}
}
return null;
}
void Bind_Main_Panel(GameObject instantiated)
{
if (instantiated == null)
@@ -9,6 +9,8 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
public static AllyHeroDeployLedger Instance { get; private set; }
private readonly Dictionary<int, int> deployCountsByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, int> finishCountsByHeroId = new Dictionary<int, int>();
private readonly Dictionary<int, int> mvpCountsByHeroId = new Dictionary<int, int>();
private bool initialized;
private bool loadedFromSave;
@@ -69,7 +71,6 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
loadedFromSave = AllyHeroDeployLedgerStorage.TryLoad(out payload);
RebuildFromPayload(payload);
initialized = true;
SeedFromHeroAssetsIfNeeded();
SyncAllMirrorFlags();
SaveNow();
}
@@ -81,6 +82,20 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
return deployCountsByHeroId.TryGetValue(heroId, out count) ? count : 0;
}
public int GetFinishCount(int heroId)
{
InitializeIfNeeded();
int count;
return finishCountsByHeroId.TryGetValue(heroId, out count) ? count : 0;
}
public int GetMvpCount(int heroId)
{
InitializeIfNeeded();
int count;
return mvpCountsByHeroId.TryGetValue(heroId, out count) ? count : 0;
}
public void IncrementDeployCount(AllyHero_SO hero, int amount = 1)
{
if (hero == null || hero.ally_heroID <= 0 || amount <= 0)
@@ -97,6 +112,38 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
SaveNow();
}
public void IncrementFinishCount(AllyHero_SO hero, int amount = 1)
{
if (hero == null || hero.ally_heroID <= 0 || amount <= 0)
{
return;
}
InitializeIfNeeded();
int current = GetFinishCount(hero.ally_heroID);
long next = (long)current + amount;
finishCountsByHeroId[hero.ally_heroID] = next > int.MaxValue ? int.MaxValue : (int)next;
hero.ally_finishCount = finishCountsByHeroId[hero.ally_heroID];
MarkDirty(hero);
SaveNow();
}
public void IncrementMvpCount(AllyHero_SO hero, int amount = 1)
{
if (hero == null || hero.ally_heroID <= 0 || amount <= 0)
{
return;
}
InitializeIfNeeded();
int current = GetMvpCount(hero.ally_heroID);
long next = (long)current + amount;
mvpCountsByHeroId[hero.ally_heroID] = next > int.MaxValue ? int.MaxValue : (int)next;
hero.ally_mvpCount = mvpCountsByHeroId[hero.ally_heroID];
MarkDirty(hero);
SaveNow();
}
public void SaveNow()
{
if (!initialized)
@@ -111,6 +158,8 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
private void RebuildFromPayload(AllyHeroDeployLedgerPayload payload)
{
deployCountsByHeroId.Clear();
finishCountsByHeroId.Clear();
mvpCountsByHeroId.Clear();
if (payload == null || payload.entries == null)
{
return;
@@ -125,39 +174,8 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
}
deployCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.deployCount);
}
}
private void SeedFromHeroAssetsIfNeeded()
{
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
bool changed = false;
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null || hero.ally_heroID <= 0)
{
continue;
}
if (deployCountsByHeroId.ContainsKey(hero.ally_heroID))
{
continue;
}
if (hero.ally_battleDeployCount <= 0)
{
continue;
}
deployCountsByHeroId[hero.ally_heroID] = Mathf.Max(0, hero.ally_battleDeployCount);
changed = true;
}
if (changed && !loadedFromSave)
{
SaveNow();
finishCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.finishCount);
mvpCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.mvpCount);
}
}
@@ -178,25 +196,71 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
count = 0;
}
if (hero.ally_battleDeployCount == count)
int finishCount;
if (!finishCountsByHeroId.TryGetValue(hero.ally_heroID, out finishCount))
{
continue;
finishCount = 0;
}
hero.ally_battleDeployCount = count;
MarkDirty(hero);
int mvpCount;
if (!mvpCountsByHeroId.TryGetValue(hero.ally_heroID, out mvpCount))
{
mvpCount = 0;
}
bool changed = false;
if (hero.ally_battleDeployCount != count)
{
hero.ally_battleDeployCount = count;
changed = true;
}
if (hero.ally_finishCount != finishCount)
{
hero.ally_finishCount = finishCount;
changed = true;
}
if (hero.ally_mvpCount != mvpCount)
{
hero.ally_mvpCount = mvpCount;
changed = true;
}
if (changed)
{
MarkDirty(hero);
}
}
}
private AllyHeroDeployLedgerPayload BuildPayload()
{
AllyHeroDeployLedgerPayload payload = AllyHeroDeployLedgerStorage.CreateDefaultPayload();
HashSet<int> allHeroIds = new HashSet<int>();
foreach (KeyValuePair<int, int> pair in deployCountsByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (KeyValuePair<int, int> pair in finishCountsByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (KeyValuePair<int, int> pair in mvpCountsByHeroId)
{
allHeroIds.Add(pair.Key);
}
foreach (int heroId in allHeroIds)
{
payload.entries.Add(new AllyHeroDeployEntry
{
heroId = pair.Key,
deployCount = Mathf.Max(0, pair.Value)
heroId = heroId,
deployCount = Mathf.Max(0, GetDeployCount(heroId)),
finishCount = Mathf.Max(0, GetFinishCount(heroId)),
mvpCount = Mathf.Max(0, GetMvpCount(heroId))
});
}
@@ -206,7 +270,7 @@ public sealed class AllyHeroDeployLedger : MonoBehaviour
private static void MarkDirty(AllyHero_SO hero)
{
#if UNITY_EDITOR
if (hero != null)
if (!Application.isPlaying && hero != null)
{
EditorUtility.SetDirty(hero);
}
@@ -6,6 +6,8 @@ public class AllyHeroDeployEntry
{
public int heroId;
public int deployCount;
public int finishCount;
public int mvpCount;
}
[Serializable]
@@ -82,16 +82,7 @@ public sealed class DushMaterialLedger : MonoBehaviour
InitializeIfNeeded();
boundPlayerData = playerData;
if (!loadedFromSave)
{
SeedFromPlayerSo(playerData, false);
loadedFromSave = true;
}
else
{
SyncToPlayerData();
}
SyncToPlayerData();
}
public int GetCount(DushMaterialKind kind)
@@ -93,16 +93,7 @@ public sealed class ExpBottleLedger : MonoBehaviour
InitializeIfNeeded();
boundPlayerData = playerData;
if (!loadedFromSave)
{
SeedFromPlayerSo(playerData, false);
loadedFromSave = true;
}
else
{
SyncToPlayerData();
}
SyncToPlayerData();
}
public int GetCount(ExpBottleKind kind)
@@ -83,15 +83,8 @@ public sealed class PlayerEconomyLedger : MonoBehaviour
InitializeIfNeeded();
boundPlayerData = playerData;
if (!loadedFromSave)
{
payload.coins = Mathf.Max(0, playerData.Coins);
payload.material = Mathf.Max(0, playerData.Material);
SaveNow();
loadedFromSave = true;
}
SyncToPlayerData();
GlobalAchievementService.EnsureInstance().ReportCurrentCoins(payload.coins);
NotifyEconomyChanged();
}
@@ -128,6 +121,7 @@ public sealed class PlayerEconomyLedger : MonoBehaviour
InitializeIfNeeded();
long next = (long)payload.coins + amount;
payload.coins = next > int.MaxValue ? int.MaxValue : (int)next;
GlobalAchievementService.EnsureInstance().ReportCoinsEarned(amount);
SaveNow();
}
@@ -175,6 +169,7 @@ public sealed class PlayerEconomyLedger : MonoBehaviour
InitializeIfNeededForSave();
PlayerEconomyStorage.TrySave(payload);
SyncToPlayerData();
GlobalAchievementService.EnsureInstance().ReportCurrentCoins(payload.coins);
NotifyEconomyChanged();
}
@@ -5,12 +5,19 @@ using System.Collections.Generic;
public class RecentPlayRecord
{
public string playedAt;
public string playedAtUtc;
public int songID;
public string songName;
public string difficultyDisplay;
public float accuracy;
public float srks;
public int totalScore;
public int chartScore;
public int idolScore;
public bool hasScoreBreakdown;
public int rankIndex;
public int rankTierCount;
public bool hasRankMarker;
public bool scoreReadable;
public bool wasEarlySettlement;
public bool wasAllPerfect;
@@ -14,6 +14,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
private readonly Dictionary<int, StoreOwnershipEntry> entriesByItemId = new Dictionary<int, StoreOwnershipEntry>();
private readonly List<storeItemSO> cachedStoreItems = new List<storeItemSO>();
private bool initialized;
private int lastSaveFrame = -1;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
@@ -68,13 +69,12 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
return;
}
initialized = true;
StoreOwnershipPayload payload;
StoreOwnershipStorage.TryLoad(out payload);
RebuildFromPayload(payload);
LoadStoreItems();
SeedFromCurrentMirrorFlags();
SyncAllMirrorFlags();
initialized = true;
SaveNow();
}
@@ -137,6 +137,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
}
ApplyEntryToItem(itemSO, entry);
GlobalAchievementService.EnsureInstance().RefreshDerivedMetrics();
SaveNow();
return true;
}
@@ -154,6 +155,12 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
return;
}
if (Application.isPlaying && lastSaveFrame == Time.frameCount)
{
return;
}
lastSaveFrame = Application.isPlaying ? Time.frameCount : -1;
StoreOwnershipStorage.TrySave(CreatePayload());
}
@@ -314,6 +321,8 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
private void SyncAllMirrorFlags()
{
ResetAllMirrorFlags();
for (int i = 0; i < cachedStoreItems.Count; i++)
{
var item = cachedStoreItems[i];
@@ -332,6 +341,68 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
}
}
private void ResetAllMirrorFlags()
{
var seenHeroIds = new HashSet<int>();
var seenSongIds = new HashSet<int>();
var seenStoryIds = new HashSet<int>();
for (int i = 0; i < cachedStoreItems.Count; i++)
{
var item = cachedStoreItems[i];
if (item == null)
{
continue;
}
if (item.itemType == storeItemSO.ItemType.character && item.associatedAllyHero != null && seenHeroIds.Add(item.associatedAllyHero.ally_heroID))
{
if (item.associatedAllyHero.isUnlocked)
{
item.associatedAllyHero.isUnlocked = false;
MarkDirty(item.associatedAllyHero);
}
}
else if (item.itemType == storeItemSO.ItemType.song && item.associatedSong != null && seenSongIds.Add(item.associatedSong.songID))
{
if (item.associatedSong.isUnlocked)
{
item.associatedSong.isUnlocked = false;
MarkDirty(item.associatedSong);
}
}
else if (item.itemType == storeItemSO.ItemType.storyPassage && item.associatedStoryPassage != null && seenStoryIds.Add(item.associatedStoryPassage.class_id))
{
var story = item.associatedStoryPassage;
bool changed = false;
if (story.isUnlocked)
{
story.isUnlocked = false;
changed = true;
}
if (story.sonList != null)
{
for (int sonIndex = 0; sonIndex < story.sonList.Count; sonIndex++)
{
var son = story.sonList[sonIndex];
if (son != null && son.son_isUnlocked)
{
son.son_isUnlocked = false;
changed = true;
}
}
}
if (changed)
{
MarkDirty(story);
}
}
}
}
private bool ValidateGrantTarget(storeItemSO itemSO, out string failureMessage)
{
failureMessage = string.Empty;
@@ -647,7 +718,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
private static void MarkDirty(UnityEngine.Object target)
{
#if UNITY_EDITOR
if (target != null)
if (!Application.isPlaying && target != null)
{
EditorUtility.SetDirty(target);
}
@@ -150,6 +150,7 @@ public class settlementController : MonoBehaviour
private Vector3 cachedIntroMvpBaseLocalPos;
private bool hasCachedIntroMvpBaseLocalPos;
private bool settlementHistoryRecorded;
private bool settlementHeroStatsRecorded;
private void Awake()
{
@@ -187,6 +188,7 @@ public class settlementController : MonoBehaviour
RegisterCurrentLineupDeployCount();
settlementHistoryRecorded = false;
settlementHeroStatsRecorded = false;
}
private void Update()
@@ -303,6 +305,179 @@ public class settlementController : MonoBehaviour
}
}
private void RecordSettlementHeroStats()
{
if (settlementHeroStatsRecorded)
{
return;
}
settlementHeroStatsRecorded = true;
List<AllyHero_SO> lineupHeroes = ResolveCurrentLineupHeroSOs();
for (int i = 0; i < lineupHeroes.Count; i++)
{
AllyHero_SO hero = lineupHeroes[i];
if (hero == null)
{
continue;
}
hero.IncrementFinishCount();
}
AllyHero_SO mvpHero = ResolveMvpHeroFromTopScorer();
if (mvpHero != null)
{
mvpHero.IncrementMvpCount();
}
}
private List<AllyHero_SO> ResolveCurrentLineupHeroSOs()
{
List<AllyHero_SO> result = new List<AllyHero_SO>();
HashSet<int> uniqueHeroIds = new HashSet<int>();
if (teamUIController.Instance != null && teamUIController.Instance.allySlotIds != null)
{
for (int i = 0; i < teamUIController.Instance.allySlotIds.Count; i++)
{
int heroId = teamUIController.Instance.allySlotIds[i];
if (heroId > 0)
{
uniqueHeroIds.Add(heroId);
}
}
}
if (uniqueHeroIds.Count == 0)
{
for (int slotIndex = 1; slotIndex <= 5; slotIndex++)
{
int heroId = PlayerPrefs.GetInt("selected_heroSlot0" + slotIndex + "_heroID", 0);
if (heroId > 0)
{
uniqueHeroIds.Add(heroId);
}
}
}
EnsureCachedAllyHeroSOs();
foreach (int heroId in uniqueHeroIds)
{
AllyHero_SO hero = FindAllyHeroSOById(heroId);
if (hero != null)
{
result.Add(hero);
}
}
return result;
}
private AllyHero_SO ResolveMvpHeroFromTopScorer()
{
int topIndex = -1;
int[] scores = new int[5];
if (sm != null)
{
scores[0] = sm.red_idolScore_sum;
scores[1] = sm.green_idolScore_sum;
scores[2] = sm.yellow_idolScore_sum;
scores[3] = sm.purple_idolScore_sum;
scores[4] = sm.blue_idolScore_sum;
}
else
{
for (int i = 0; i < 5; i++)
{
GameObject go = GameObject.Find($"ally_0{i + 1}");
if (go != null)
{
AllyCombatant ac = go.GetComponent<AllyCombatant>();
scores[i] = ac != null ? ac.currentScore : 0;
}
else
{
scores[i] = 0;
}
}
}
int max = -1;
for (int i = 0; i < scores.Length; i++)
{
if (scores[i] > max)
{
max = scores[i];
topIndex = i;
}
}
if (topIndex < 0 || max <= 0)
{
return null;
}
AllyHero_SO heroSO = null;
if (SkillBuilder.Instance != null)
{
try { heroSO = SkillBuilder.Instance.GetAllyHeroSOBySlot(topIndex); }
catch { heroSO = null; }
}
if (heroSO == null && teamUIController.Instance != null && teamUIController.Instance.allySlotIds != null)
{
int allyId = -1;
if (topIndex >= 0 && topIndex < teamUIController.Instance.allySlotIds.Count)
{
allyId = teamUIController.Instance.allySlotIds[topIndex];
}
if (allyId > 0)
{
heroSO = FindAllyHeroSOById(allyId);
}
}
return heroSO;
}
private static void EnsureCachedAllyHeroSOs()
{
if (_cachedAllyHeroSOs == null || _cachedAllyHeroSOs.Length == 0)
{
_cachedAllyHeroSOs = Resources.LoadAll<AllyHero_SO>(string.Empty);
}
}
private static AllyHero_SO FindAllyHeroSOById(int allyId)
{
if (allyId <= 0)
{
return null;
}
EnsureCachedAllyHeroSOs();
if (_cachedAllyHeroSOs == null)
{
return null;
}
for (int i = 0; i < _cachedAllyHeroSOs.Length; i++)
{
AllyHero_SO hero = _cachedAllyHeroSOs[i];
if (hero != null && hero.ally_heroID == allyId)
{
return hero;
}
}
return null;
}
public void startSettlement_uiUpdate()
{
if (settlementUiInitialized)
@@ -312,6 +487,7 @@ public class settlementController : MonoBehaviour
}
settlementUiInitialized = true;
settlementHistoryRecorded = false;
settlementHeroStatsRecorded = false;
// Documentation text normalized.
InitializeSettlementCanvas();
@@ -518,6 +694,7 @@ public class settlementController : MonoBehaviour
// Documentation text normalized.
// Documentation text normalized.
RecordSettlementHeroStats();
SetMvpHeroImageFromTopScorer();
StartMusicTransition();
@@ -537,6 +714,7 @@ public class settlementController : MonoBehaviour
var record = new RecentPlayRecord
{
playedAt = System.DateTime.Now.ToString("MM-dd, HH:mm"),
playedAtUtc = System.DateTime.UtcNow.ToString("o"),
songID = thisSong_so != null ? thisSong_so.songID : 0,
songName = thisSong_so != null && !string.IsNullOrWhiteSpace(thisSong_so.songName)
? thisSong_so.songName
@@ -545,6 +723,12 @@ public class settlementController : MonoBehaviour
accuracy = targetAccuracyPercent,
srks = CalculateSongRankingScore(noteCountRaw),
totalScore = targetTotalScore,
chartScore = targetPmScore,
idolScore = targetIdolScore,
hasScoreBreakdown = true,
rankIndex = CalculateRankIndex(targetTotalScore),
rankTierCount = GetRankTierCount(),
hasRankMarker = true,
scoreReadable = true,
wasEarlySettlement = IsEarlySettlement(noteCountRaw),
wasAllPerfect = IsAllPerfectRun(noteCountRaw)
@@ -598,6 +782,48 @@ public class settlementController : MonoBehaviour
}
}
private int GetRankTierCount()
{
if (rankConfig == null || rankConfig.thresholds == null)
{
return 0;
}
return Mathf.Max(0, rankConfig.thresholds.Count);
}
private int CalculateRankIndex(int score)
{
if (rankConfig == null || rankConfig.thresholds == null || rankConfig.thresholds.Count == 0 || score <= 0)
{
return 0;
}
int bestLevel = 0;
float highestMatchingPercent = -1f;
int thresholdCount = rankConfig.thresholds.Count;
for (int i = 0; i < thresholdCount; i++)
{
RankThreshold threshold = rankConfig.thresholds[i];
if (threshold == null)
{
continue;
}
float requiredScore = rankConfig.baseScore * threshold.thresholdPercent;
if (score < requiredScore || threshold.thresholdPercent <= highestMatchingPercent)
{
continue;
}
highestMatchingPercent = threshold.thresholdPercent;
bestLevel = thresholdCount - i;
}
return bestLevel;
}
private float CalculateSongRankingScore(int noteCountRaw)
{
int totalNotes = Mathf.Max(1, noteCountRaw);