using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Bansonic; public class equipIllusion : MonoBehaviour { [Header("so")] public equipmentSO eqp_p_SO; public Player_SO playerSO; public eUpdate_mtrSO need_mtrSO; public equipmentRandomConfigSO randomConfig; [Header("levelList")] public string[] illusionLevelName; [Header("statusObj")] public GameObject beforeObj; public GameObject toObj; public GameObject nextObj; [Header("dragging")] public GameObject dragPrev; [Header("transforms")] public Transform prevIllusionEquip; public Transform nextIllusionEquip; public GameObject eip; [Header("texts")] public Text prevLevelText; public Text nextLevelText; public Text reasonWhy; [Header("materials")] public GameObject mtrPrefab; public Transform merParent; public Text mtrInfo; [Header("sprites")] public Sprite memoryFragmentSprite; public Sprite coinSprite; [Header("buttons")] public Button yesIllusionButton; private GameObject spawnedPrevLevelInstance; private GameObject spawnedNextLevelInstance; private equipmentSO nextLevelPreviewSO; private readonly List spawnedMaterialItems = new List(); private void Awake() { InitializeBindings(); SetDragPreviewVisible(false); RefreshAll(); } private void OnEnable() { InitializeBindings(); SetDragPreviewVisible(false); RefreshAll(); } private void OnDisable() { if (yesIllusionButton != null) { yesIllusionButton.onClick.RemoveListener(HandleYesIllusionClicked); } } private void InitializeBindings() { if (playerSO != null) { PlayerEconomyLedger.EnsureInstance().AttachPlayerData(playerSO); } if (randomConfig != null) { randomConfig.NormalizeRuntimeData(); } if (yesIllusionButton != null) { yesIllusionButton.onClick.RemoveListener(HandleYesIllusionClicked); yesIllusionButton.onClick.AddListener(HandleYesIllusionClicked); } } public void SetDragPreviewVisible(bool visible) { if (dragPrev == null) { return; } if (!gameObject.activeInHierarchy) { dragPrev.SetActive(false); return; } dragPrev.SetActive(visible); } public bool IsPointerOverDropArea(Vector2 screenPoint, Camera eventCamera) { if (dragPrev == null || !dragPrev.activeInHierarchy) { return false; } RectTransform rectTransform = dragPrev.transform as RectTransform; if (rectTransform == null) { return false; } return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, screenPoint, eventCamera); } public void AcceptDraggedEquipment(equipmentSO equipment) { if (equipment == null) { return; } eqp_p_SO = equipment; RefreshAll(); } private void RefreshAll() { RefreshPrevLevelPreview(); RefreshNextLevelPreview(); RefreshSelectionState(); RefreshPrevLevelText(); RefreshNextLevelText(); RefreshMaterialRequirements(); RefreshReasonWhy(); RefreshActionState(); } private void RefreshPrevLevelPreview() { DestroyPrevLevelPreview(); if (eqp_p_SO == null || eip == null || prevIllusionEquip == null) { return; } GameObject instance = Instantiate(eip, prevIllusionEquip); instance.name = $"{eqp_p_SO.name}_PrevTour"; instance.SetActive(true); spawnedPrevLevelInstance = instance; equipItemPrefab item = instance.GetComponent(); if (item != null) { item.Bind(eqp_p_SO); item.SetInteractionOptions(false, false); } } private void RefreshNextLevelPreview() { DestroyNextLevelPreview(); nextLevelPreviewSO = null; if (!CanShowNextPreview()) { return; } nextLevelPreviewSO = Instantiate(eqp_p_SO); nextLevelPreviewSO.level = Mathf.Clamp(eqp_p_SO.level + 1, 0, 20); nextLevelPreviewSO.name = $"{eqp_p_SO.name}(TourPreview)"; GameObject instance = Instantiate(eip, nextIllusionEquip); instance.name = $"{eqp_p_SO.name}_NextTour"; instance.SetActive(true); spawnedNextLevelInstance = instance; equipItemPrefab item = instance.GetComponent(); if (item != null) { item.Bind(nextLevelPreviewSO); item.SetInteractionOptions(false, false); } } private bool CanShowNextPreview() { return eqp_p_SO != null && eip != null && nextIllusionEquip != null && IsTourEligibleLevel(eqp_p_SO.level); } private void RefreshSelectionState() { if (beforeObj != null) { beforeObj.SetActive(true); } bool showRightSide = eqp_p_SO != null && IsTourEligibleLevel(eqp_p_SO.level); if (toObj != null) { toObj.SetActive(showRightSide); } if (nextObj != null) { nextObj.SetActive(showRightSide); } } private void RefreshPrevLevelText() { if (prevLevelText == null) { return; } if (eqp_p_SO == null) { prevLevelText.text = "选择一个记忆"; prevLevelText.color = Color.black; return; } prevLevelText.text = ResolveTourStageName(eqp_p_SO.level); prevLevelText.color = ResolveEquipmentColor(eqp_p_SO.GetVisualQualityColorIndex()); } private void RefreshNextLevelText() { if (nextLevelText == null) { return; } if (!CanShowNextPreview() || nextLevelPreviewSO == null) { nextLevelText.text = string.Empty; return; } nextLevelText.text = ResolveTourStageName(nextLevelPreviewSO.level); nextLevelText.color = ResolveEquipmentColor(nextLevelPreviewSO.GetVisualQualityColorIndex()); } private void RefreshMaterialRequirements() { ClearSpawnedMaterialItems(); if (mtrInfo == null) { return; } if (eqp_p_SO == null || need_mtrSO == null) { mtrInfo.text = "需要选择一个记忆"; return; } int level = eqp_p_SO.level; if (!IsLegalLevel(level)) { mtrInfo.text = "非法等级"; return; } if (level >= 20) { mtrInfo.text = "已完成全部巡演"; return; } if (!IsTourEligibleLevel(level)) { mtrInfo.text = BuildTourRequirementText(level); return; } mtrInfo.text = string.Empty; SpawnTourMaterialItems(level); } private void SpawnTourMaterialItems(int level) { if (mtrPrefab == null || merParent == null || need_mtrSO == null) { return; } int materialRequired = need_mtrSO.GetBreakthroughMaterialRequired(level); int coinRequired = need_mtrSO.GetBreakthroughCoinsRequired(level); int memoryRequired = materialRequired; if (materialRequired > 0 && need_mtrSO.breakthroughMaterial != null) { SpawnMaterialItem( need_mtrSO.breakthroughMaterial.consumableSprite, need_mtrSO.breakthroughMaterial.consumableName, EquipmentConsumableLedger.EnsureInstance().GetCount(need_mtrSO.breakthroughMaterial.consumableKind), materialRequired); } if (coinRequired > 0) { SpawnMaterialItem( coinSprite, "算力", PlayerEconomyLedger.EnsureInstance().GetCoins(), coinRequired); } if (memoryRequired > 0) { SpawnMaterialItem( memoryFragmentSprite, "记忆碎片", PlayerEconomyLedger.EnsureInstance().GetMaterial(), memoryRequired); } } private void SpawnMaterialItem(Sprite sprite, string displayName, int ownedAmount, int requiredAmount) { GameObject instance = Instantiate(mtrPrefab, merParent); instance.SetActive(true); spawnedMaterialItems.Add(instance); materialPrefab item = instance.GetComponent(); if (item != null) { item.BindOwnedRequired(sprite, displayName, ownedAmount, requiredAmount, true, false, null); item.SetSelected(true); if (item.materialButton != null) { item.materialButton.interactable = false; } } } private void RefreshReasonWhy() { if (reasonWhy == null) { return; } reasonWhy.text = GetTourFailureReason(); } private void RefreshActionState() { if (yesIllusionButton == null) { return; } yesIllusionButton.interactable = string.IsNullOrEmpty(GetTourFailureReason()); } private string GetTourFailureReason() { if (eqp_p_SO == null) { return string.Empty; } if (!IsLegalLevel(eqp_p_SO.level)) { return "非法等级"; } if (eqp_p_SO.level >= 20) { return "此记忆已完成全部巡演"; } if (!IsTourEligibleLevel(eqp_p_SO.level)) { return BuildTourRequirementText(eqp_p_SO.level); } if (need_mtrSO == null) { return "未配置巡演规则"; } if (randomConfig == null) { return "未配置随机规则"; } int materialRequired = need_mtrSO.GetBreakthroughMaterialRequired(eqp_p_SO.level); int coinRequired = need_mtrSO.GetBreakthroughCoinsRequired(eqp_p_SO.level); int memoryRequired = materialRequired; if (need_mtrSO.breakthroughMaterial == null) { return "未配置巡演材料"; } if (materialRequired > 0 && EquipmentConsumableLedger.EnsureInstance().GetCount(need_mtrSO.breakthroughMaterial.consumableKind) < materialRequired) { return "巡演材料不足"; } if (!PlayerEconomyLedger.EnsureInstance().HasEnoughCoins(coinRequired)) { return "算力不足"; } if (!PlayerEconomyLedger.EnsureInstance().HasEnoughMaterial(memoryRequired)) { return "记忆碎片不足"; } return string.Empty; } private void HandleYesIllusionClicked() { string failureReason = GetTourFailureReason(); if (!string.IsNullOrEmpty(failureReason)) { RefreshAll(); return; } int level = eqp_p_SO.level; int materialRequired = need_mtrSO.GetBreakthroughMaterialRequired(level); int coinRequired = need_mtrSO.GetBreakthroughCoinsRequired(level); int memoryRequired = materialRequired; if (!EquipmentConsumableLedger.EnsureInstance().TryConsume(need_mtrSO.breakthroughMaterial.consumableKind, materialRequired)) { RefreshAll(); return; } if (!PlayerEconomyLedger.EnsureInstance().TrySpendCoins(coinRequired)) { EquipmentConsumableLedger.EnsureInstance().Add(need_mtrSO.breakthroughMaterial.consumableKind, materialRequired); RefreshAll(); return; } if (!PlayerEconomyLedger.EnsureInstance().TrySpendMaterial(memoryRequired)) { EquipmentConsumableLedger.EnsureInstance().Add(need_mtrSO.breakthroughMaterial.consumableKind, materialRequired); PlayerEconomyLedger.EnsureInstance().AddCoins(coinRequired); RefreshAll(); return; } ApplyTourEffect(); eqp_p_SO.level = Mathf.Clamp(eqp_p_SO.level + 1, 0, 20); PersistEquipment(eqp_p_SO); RefreshAllEquipBags(); RefreshAll(); } private void ApplyTourEffect() { if (eqp_p_SO == null || randomConfig == null) { return; } randomConfig.NormalizeRuntimeData(); int skillRollIndex = GetTourSkillRollIndex(eqp_p_SO.level); bool skillHit = skillRollIndex >= 0 && UnityEngine.Random.value <= GetIllusionSkillChance(skillRollIndex); if (skillHit && eqp_p_SO.ia_skillID <= 0 && randomConfig.TryGetIllusionSkillId(out int illusionSkillId) && illusionSkillId > 0) { eqp_p_SO.ia_skillID = illusionSkillId; return; } List candidates = BuildIllusionEffectPool(); if (candidates.Count == 0) { return; } equipmentSO.EquipmentSpecialEffectType rolledType = candidates[UnityEngine.Random.Range(0, candidates.Count)]; if (!randomConfig.TryGetIllusionEffectRange(rolledType, out float minValue, out float maxValue)) { return; } float rolledValue = RollGaussianValue(minValue, maxValue); MergeIllusionEffect(rolledType, rolledValue); } private void MergeIllusionEffect(equipmentSO.EquipmentSpecialEffectType effectType, float rolledValue) { var effects = new List(); if (eqp_p_SO.illusionEffects != null) { for (int i = 0; i < eqp_p_SO.illusionEffects.Length; i++) { equipmentSO.EquipmentSpecialEffect effect = eqp_p_SO.illusionEffects[i]; if (effect != null) { effects.Add(new equipmentSO.EquipmentSpecialEffect { effectType = effect.effectType, value = effect.value }); } } } float bestExistingValue = float.MinValue; bool hasDuplicate = false; for (int i = effects.Count - 1; i >= 0; i--) { if (effects[i].effectType != effectType) { continue; } hasDuplicate = true; bestExistingValue = Mathf.Max(bestExistingValue, effects[i].value); effects.RemoveAt(i); } if (hasDuplicate) { float best = Mathf.Max(bestExistingValue, rolledValue); effects.Add(new equipmentSO.EquipmentSpecialEffect { effectType = effectType, value = best + Mathf.Abs(best) * 0.2f }); eqp_p_SO.illusionEffects = NormalizeIllusionEffects(effects); return; } effects.Add(new equipmentSO.EquipmentSpecialEffect { effectType = effectType, value = rolledValue }); eqp_p_SO.illusionEffects = NormalizeIllusionEffects(effects); } private equipmentSO.EquipmentSpecialEffect[] NormalizeIllusionEffects(List effects) { if (effects == null || effects.Count == 0) { return System.Array.Empty(); } eqp_p_SO.illusionEffects = effects.ToArray(); return eqp_p_SO.GetNormalizedIllusionEffects(); } private List BuildIllusionEffectPool() { var pool = new List(); equipmentSO.EquipmentSpecialEffectType[] allTypes = { equipmentSO.EquipmentSpecialEffectType.MaxHp, equipmentSO.EquipmentSpecialEffectType.Attack, equipmentSO.EquipmentSpecialEffectType.MaxMana, equipmentSO.EquipmentSpecialEffectType.DamageResistance, equipmentSO.EquipmentSpecialEffectType.ScoreEfficiency, equipmentSO.EquipmentSpecialEffectType.HitManaRecovery, equipmentSO.EquipmentSpecialEffectType.HitDamageMultiplier, equipmentSO.EquipmentSpecialEffectType.HpLoseBase }; for (int i = 0; i < allTypes.Length; i++) { if (randomConfig.TryGetIllusionEffectRange(allTypes[i], out _, out _)) { pool.Add(allTypes[i]); } } return pool; } private int GetTourSkillRollIndex(int level) { switch (level) { case 4: return 0; case 9: return 1; case 14: return 2; case 19: return 3; default: return -1; } } private float GetIllusionSkillChance(int rollIndex) { switch (rollIndex) { case 0: return randomConfig != null ? randomConfig.ies01 : 0f; case 1: return randomConfig != null ? randomConfig.ies02 : 0f; case 2: return randomConfig != null ? randomConfig.ies03 : 0f; case 3: return randomConfig != null ? randomConfig.ies04 : 0f; default: return 0f; } } private static float RollGaussianValue(float minValue, float maxValue) { if (Mathf.Approximately(minValue, maxValue)) { return AvoidZero(minValue, maxValue, minValue); } if (maxValue < minValue) { float cached = minValue; minValue = maxValue; maxValue = cached; } float u1 = Mathf.Max(Mathf.Clamp01(UnityEngine.Random.value), 1e-6f); float u2 = Mathf.Clamp01(UnityEngine.Random.value); float standard = Mathf.Sqrt(-2f * Mathf.Log(u1)) * Mathf.Cos(2f * Mathf.PI * u2); float mean = (minValue + maxValue) * 0.5f; float deviation = (maxValue - minValue) / 6f; float rolled = Mathf.Clamp(mean + standard * deviation, minValue, maxValue); return AvoidZero(minValue, maxValue, rolled); } private static float AvoidZero(float minValue, float maxValue, float value) { if (!Mathf.Approximately(value, 0f)) { return value; } if (minValue > 0f) { return minValue; } if (maxValue < 0f) { return maxValue; } const float epsilon = 0.0001f; return UnityEngine.Random.value < 0.5f ? -epsilon : epsilon; } private void ClearSpawnedMaterialItems() { for (int i = spawnedMaterialItems.Count - 1; i >= 0; i--) { GameObject instance = spawnedMaterialItems[i]; if (instance == null) { continue; } if (Application.isPlaying) { Destroy(instance); } else { DestroyImmediate(instance); } } spawnedMaterialItems.Clear(); } private void DestroyPrevLevelPreview() { if (spawnedPrevLevelInstance == null) { return; } if (Application.isPlaying) { Destroy(spawnedPrevLevelInstance); } else { DestroyImmediate(spawnedPrevLevelInstance); } spawnedPrevLevelInstance = null; } private void DestroyNextLevelPreview() { if (spawnedNextLevelInstance != null) { if (Application.isPlaying) { Destroy(spawnedNextLevelInstance); } else { DestroyImmediate(spawnedNextLevelInstance); } } spawnedNextLevelInstance = null; if (nextLevelPreviewSO != null) { if (Application.isPlaying) { Destroy(nextLevelPreviewSO); } else { DestroyImmediate(nextLevelPreviewSO); } } nextLevelPreviewSO = null; } private static bool IsLegalLevel(int level) { return level >= 0 && level <= 20; } private static bool IsTourEligibleLevel(int level) { return level == 4 || level == 9 || level == 14 || level == 19; } private static string BuildTourRequirementText(int currentLevel) { int requiredLevel = GetNextTourRequiredLevel(currentLevel); return $"需要追忆等级{requiredLevel},当前追忆等级{Mathf.Max(0, currentLevel)}"; } private static int GetNextTourRequiredLevel(int currentLevel) { if (currentLevel < 4) { return 4; } if (currentLevel < 9) { return 9; } if (currentLevel < 14) { return 14; } if (currentLevel < 19) { return 19; } return 19; } private string ResolveTourStageName(int level) { if (!IsLegalLevel(level)) { return "非法巡演罪"; } int index; if (level <= 4) index = 0; else if (level <= 9) index = 1; else if (level <= 14) index = 2; else if (level <= 19) index = 3; else index = 4; if (illusionLevelName != null && index >= 0 && index < illusionLevelName.Length && !string.IsNullOrWhiteSpace(illusionLevelName[index])) { return illusionLevelName[index]; } return $"{Mathf.Max(0, level)}追忆等级"; } private Color ResolveEquipmentColor(int colorIndex) { if (eip == null) { return Color.white; } equipItemPrefab itemPrefab = eip.GetComponent(); if (itemPrefab == null) { return Color.white; } return itemPrefab.GetAccentColorByVisualIndex(colorIndex); } private static void PersistEquipment(equipmentSO equipment) { if (equipment == null) { return; } if (Application.isPlaying) { equipmentGenerator.SaveRuntimeEquipmentState(equipment); return; } #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(equipment); if (!Application.isPlaying) { UnityEditor.AssetDatabase.SaveAssets(); } #endif } private static void RefreshAllEquipBags() { equipBag[] bags = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < bags.Length; i++) { if (bags[i] != null) { bags[i].Rebuild(); } } } }