Files
2026-07-30 23:15:58 +08:00

852 lines
23 KiB
C#

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<GameObject> spawnedMaterialItems = new List<GameObject>();
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<equipItemPrefab>();
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<equipItemPrefab>();
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<materialPrefab>();
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);
// ApplyTourEffect 可能原地写入 ia_skillID(装备 SO 引用不变),
// 若该装备正被某英雄装备,其有效技能组缓存需失效重建。
AllyHero_SO.InvalidateEquipmentDerivedCaches();
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<equipmentSO.EquipmentSpecialEffectType> 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<equipmentSO.EquipmentSpecialEffect>();
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<equipmentSO.EquipmentSpecialEffect> effects)
{
if (effects == null || effects.Count == 0)
{
return System.Array.Empty<equipmentSO.EquipmentSpecialEffect>();
}
eqp_p_SO.illusionEffects = effects.ToArray();
return eqp_p_SO.GetNormalizedIllusionEffects();
}
private List<equipmentSO.EquipmentSpecialEffectType> BuildIllusionEffectPool()
{
var pool = new List<equipmentSO.EquipmentSpecialEffectType>();
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<equipItemPrefab>();
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<equipBag>(FindObjectsInactive.Include, FindObjectsSortMode.None);
for (int i = 0; i < bags.Length; i++)
{
if (bags[i] != null)
{
bags[i].Rebuild();
}
}
}
}