using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Bansonic; #if UNITY_EDITOR using UnityEditor; #endif public class equipTransfer : MonoBehaviour { public enum TransferMode { MemoryEntrust = 0, TourEntrust = 1 } public enum TransferDropSlot { None = 0, Main = 1, Need = 2 } [Header("so")] public Player_SO playerSO; public equipmentSO eqp_p_SO; public equipmentSO eqp_need_SO; public eUpdate_mtrSO need_mtrSO; [Header("sprites")] public Sprite memoryFragment; public Sprite coins; [Header("state objects")] public GameObject mainEquipObj; public GameObject toObj; public GameObject needEquipObj; public GameObject previewObj; [Header("drag")] public GameObject dragMainEquip; public GameObject dragNeedEquip; [Header("transforms")] public Transform mainEquip; public Transform needEquip; public Transform previewEquip; public GameObject eip; [Header("choose")] public Dropdown chooseType; [Header("texts")] public Text mainEquipText; public Text needEquipText; public Text previewEquipText; public Text reasonWhy; [Header("materials")] public GameObject mtrPrefab; public Transform mtrParent; public Text mtrInfo; [Header("buttons")] public Button yesTransferButton; private GameObject spawnedMainEquipInstance; private GameObject spawnedNeedEquipInstance; private GameObject spawnedPreviewEquipInstance; private equipmentSO previewEquipSO; private readonly List spawnedMaterialItems = new List(); private void Awake() { InitializeBindings(); SetDragPreviewVisible(false); RefreshAll(); } private void OnEnable() { InitializeBindings(); SetDragPreviewVisible(false); RefreshAll(); } private void OnDisable() { if (yesTransferButton != null) { yesTransferButton.onClick.RemoveListener(HandleYesTransferClicked); } if (chooseType != null) { chooseType.onValueChanged.RemoveListener(HandleChooseTypeChanged); } } private void InitializeBindings() { if (playerSO != null) { PlayerEconomyLedger.EnsureInstance().AttachPlayerData(playerSO); } if (chooseType != null) { EnsureChooseTypeOptions(); chooseType.onValueChanged.RemoveListener(HandleChooseTypeChanged); chooseType.onValueChanged.AddListener(HandleChooseTypeChanged); } if (yesTransferButton != null) { yesTransferButton.onClick.RemoveListener(HandleYesTransferClicked); yesTransferButton.onClick.AddListener(HandleYesTransferClicked); } } public void SetDragPreviewVisible(bool visible) { if (!gameObject.activeInHierarchy) { if (dragMainEquip != null) { dragMainEquip.SetActive(false); } if (dragNeedEquip != null) { dragNeedEquip.SetActive(false); } return; } if (dragMainEquip != null) { dragMainEquip.SetActive(visible); } if (dragNeedEquip != null) { dragNeedEquip.SetActive(visible); } } public TransferDropSlot GetHoveredDropSlot(Vector2 screenPoint, Camera eventCamera) { if (IsPointerOverDropObject(dragMainEquip, screenPoint, eventCamera)) { return TransferDropSlot.Main; } if (IsPointerOverDropObject(dragNeedEquip, screenPoint, eventCamera)) { return TransferDropSlot.Need; } return TransferDropSlot.None; } public void AcceptDraggedEquipment(equipmentSO equipment, TransferDropSlot slot) { if (equipment == null) { return; } switch (slot) { case TransferDropSlot.Main: eqp_p_SO = equipment; break; case TransferDropSlot.Need: eqp_need_SO = equipment; break; default: return; } RefreshAll(); } private void HandleChooseTypeChanged(int _) { RefreshAll(); } private void RefreshAll() { RefreshSlotObjects(); RefreshMainEquipPreview(); RefreshNeedEquipPreview(); RefreshTransferPreview(); RefreshPreviewState(); RefreshMaterialRequirements(); RefreshReasonWhy(); RefreshActionState(); } private void RefreshSlotObjects() { if (mainEquipObj != null) { mainEquipObj.SetActive(true); } if (needEquipObj != null) { needEquipObj.SetActive(true); } } private void RefreshMainEquipPreview() { DestroyGeneratedChild(mainEquip, "_TransferMain"); DestroySpawned(ref spawnedMainEquipInstance); if (eqp_p_SO == null || eip == null || mainEquip == null) { return; } spawnedMainEquipInstance = Instantiate(eip, mainEquip); spawnedMainEquipInstance.name = $"{eqp_p_SO.name}_TransferMain"; spawnedMainEquipInstance.SetActive(true); spawnedMainEquipInstance.transform.SetAsLastSibling(); equipItemPrefab item = spawnedMainEquipInstance.GetComponent(); if (item != null) { item.Bind(eqp_p_SO); item.SetInteractionOptions(false, false); } } private void RefreshNeedEquipPreview() { DestroyGeneratedChild(needEquip, "_TransferNeed"); DestroySpawned(ref spawnedNeedEquipInstance); if (eqp_need_SO == null || eip == null || needEquip == null) { return; } spawnedNeedEquipInstance = Instantiate(eip, needEquip); spawnedNeedEquipInstance.name = $"{eqp_need_SO.name}_TransferNeed"; spawnedNeedEquipInstance.SetActive(true); spawnedNeedEquipInstance.transform.SetAsLastSibling(); equipItemPrefab item = spawnedNeedEquipInstance.GetComponent(); if (item != null) { item.Bind(eqp_need_SO); item.SetInteractionOptions(false, false); } } private void RefreshTransferPreview() { DestroyGeneratedChild(previewEquip, "_TransferPreview"); DestroySpawned(ref spawnedPreviewEquipInstance); DestroyPreviewEquipment(); string previewFailureReason = GetPreviewFailureReason(); if (!string.IsNullOrEmpty(previewFailureReason) || eip == null || previewEquip == null || eqp_p_SO == null || eqp_need_SO == null) { return; } previewEquipSO = Instantiate(eqp_p_SO); previewEquipSO.name = $"{eqp_p_SO.name}(TransferPreview)"; if (GetCurrentMode() == TransferMode.MemoryEntrust) { previewEquipSO.specialEffects = CloneEffects(eqp_need_SO.specialEffects); previewEquipSO.sa_skillID = Mathf.Max(0, eqp_need_SO.sa_skillID); } else { previewEquipSO.illusionEffects = CloneEffects(eqp_need_SO.illusionEffects); previewEquipSO.ia_skillID = Mathf.Max(0, eqp_need_SO.ia_skillID); } spawnedPreviewEquipInstance = Instantiate(eip, previewEquip); spawnedPreviewEquipInstance.name = $"{eqp_p_SO.name}_TransferPreview"; spawnedPreviewEquipInstance.SetActive(true); spawnedPreviewEquipInstance.transform.SetAsLastSibling(); equipItemPrefab item = spawnedPreviewEquipInstance.GetComponent(); if (item != null) { item.Bind(previewEquipSO); item.SetInteractionOptions(false, false); } } private void RefreshPreviewState() { string previewFailureReason = GetPreviewFailureReason(); bool hasPreview = string.IsNullOrEmpty(previewFailureReason) && spawnedPreviewEquipInstance != null; if (toObj != null) { toObj.SetActive(true); } if (previewObj != null) { previewObj.SetActive(hasPreview); } if (previewEquipText != null) { previewEquipText.text = hasPreview ? string.Empty : previewFailureReason; previewEquipText.gameObject.SetActive(!hasPreview && !string.IsNullOrEmpty(previewFailureReason)); } } private void RefreshMaterialRequirements() { ClearSpawnedMaterialItems(); if (mtrInfo == null) { return; } string materialReason = GetMaterialFailureReason(); string structuralReason = GetMaterialStructuralFailureReason(); if (!string.IsNullOrEmpty(structuralReason)) { mtrInfo.text = structuralReason; return; } mtrInfo.text = string.Empty; SpawnTransferMaterials(); } private void SpawnTransferMaterials() { if (need_mtrSO == null || mtrPrefab == null || mtrParent == null) { return; } int transferRequired; int memoryRequired; int coinRequired; GetCurrentCosts(out transferRequired, out memoryRequired, out coinRequired); if (transferRequired > 0 && need_mtrSO.propertyTransferMaterial != null) { SpawnMaterialItem( need_mtrSO.propertyTransferMaterial.consumableSprite, need_mtrSO.propertyTransferMaterial.consumableName, EquipmentConsumableLedger.EnsureInstance().GetCount(need_mtrSO.propertyTransferMaterial.consumableKind), transferRequired); } if (coinRequired > 0) { SpawnMaterialItem(coins, "算力", PlayerEconomyLedger.EnsureInstance().GetCoins(), coinRequired); } if (memoryRequired > 0) { SpawnMaterialItem(memoryFragment, "记忆碎片", PlayerEconomyLedger.EnsureInstance().GetMaterial(), memoryRequired); } } private void SpawnMaterialItem(Sprite sprite, string displayName, int ownedAmount, int requiredAmount) { GameObject instance = Instantiate(mtrPrefab, mtrParent); 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) { reasonWhy.text = GetActionFailureReason(); } } private void RefreshActionState() { if (yesTransferButton != null) { yesTransferButton.interactable = string.IsNullOrEmpty(GetActionFailureReason()); } } private string GetPreviewFailureReason() { if (eqp_p_SO == null || eqp_need_SO == null) { return "需要选择主记忆和副记忆"; } if (eqp_p_SO == eqp_need_SO) { return "不能选择同一件记忆"; } if (eqp_p_SO.skillType != eqp_need_SO.skillType) { return "需要同类型记忆"; } if (GetCurrentMode() == TransferMode.TourEntrust && GetLevelBucket(eqp_p_SO.level) != GetLevelBucket(eqp_need_SO.level)) { return "巡演嘱托需要相同巡演阶段"; } return string.Empty; } private string GetMaterialFailureReason() { int transferRequired; int memoryRequired; int coinRequired; GetCurrentCosts(out transferRequired, out memoryRequired, out coinRequired); if (transferRequired > 0 && EquipmentConsumableLedger.EnsureInstance().GetCount(need_mtrSO.propertyTransferMaterial.consumableKind) < transferRequired) { return "洗炼材料不足"; } if (!PlayerEconomyLedger.EnsureInstance().HasEnoughCoins(coinRequired)) { return "算力不足"; } if (!PlayerEconomyLedger.EnsureInstance().HasEnoughMaterial(memoryRequired)) { return "记忆碎片不足"; } return string.Empty; } private string GetMaterialStructuralFailureReason() { if (eqp_p_SO == null || eqp_need_SO == null) { return "需要选择主记忆和副记忆"; } string previewFailureReason = GetPreviewFailureReason(); if (!string.IsNullOrEmpty(previewFailureReason)) { return previewFailureReason; } if (need_mtrSO == null) { return "未配置洗炼规则"; } if (need_mtrSO.propertyTransferMaterial == null) { return "未配置洗炼材料"; } return string.Empty; } private string GetActionFailureReason() { if (eqp_p_SO == null || eqp_need_SO == null) { return "需要选择主记忆和副记忆"; } if (eqp_p_SO == eqp_need_SO) { return "不能选择同一件记忆"; } string previewFailureReason = GetPreviewFailureReason(); if (!string.IsNullOrEmpty(previewFailureReason)) { return previewFailureReason; } string structuralReason = GetMaterialStructuralFailureReason(); if (!string.IsNullOrEmpty(structuralReason)) { return structuralReason; } return GetMaterialFailureReason(); } private void HandleYesTransferClicked() { string failureReason = GetActionFailureReason(); if (!string.IsNullOrEmpty(failureReason)) { RefreshAll(); return; } int transferRequired; int memoryRequired; int coinRequired; GetCurrentCosts(out transferRequired, out memoryRequired, out coinRequired); if (transferRequired > 0 && !EquipmentConsumableLedger.EnsureInstance().TryConsume(need_mtrSO.propertyTransferMaterial.consumableKind, transferRequired)) { RefreshAll(); return; } if (coinRequired > 0 && !PlayerEconomyLedger.EnsureInstance().TrySpendCoins(coinRequired)) { if (transferRequired > 0) { EquipmentConsumableLedger.EnsureInstance().Add(need_mtrSO.propertyTransferMaterial.consumableKind, transferRequired); } RefreshAll(); return; } if (memoryRequired > 0 && !PlayerEconomyLedger.EnsureInstance().TrySpendMaterial(memoryRequired)) { if (coinRequired > 0) { PlayerEconomyLedger.EnsureInstance().AddCoins(coinRequired); } if (transferRequired > 0) { EquipmentConsumableLedger.EnsureInstance().Add(need_mtrSO.propertyTransferMaterial.consumableKind, transferRequired); } RefreshAll(); return; } if (GetCurrentMode() == TransferMode.MemoryEntrust) { eqp_p_SO.specialEffects = CloneEffects(eqp_need_SO.specialEffects); eqp_p_SO.sa_skillID = Mathf.Max(0, eqp_need_SO.sa_skillID); } else { eqp_p_SO.illusionEffects = CloneEffects(eqp_need_SO.illusionEffects); eqp_p_SO.ia_skillID = Mathf.Max(0, eqp_need_SO.ia_skillID); } // 原地修改了装备的 sa_skillID/ia_skillID(装备 SO 引用不变), // 若该装备正被某英雄装备,其有效技能组缓存需失效重建。 AllyHero_SO.InvalidateEquipmentDerivedCaches(); PersistEquipment(eqp_p_SO); DeleteEquipment(eqp_need_SO); eqp_need_SO = null; RefreshAllEquipBags(); RefreshAll(); } private void GetCurrentCosts(out int transferRequired, out int memoryRequired, out int coinRequired) { if (need_mtrSO == null) { transferRequired = 0; memoryRequired = 0; coinRequired = 0; return; } if (GetCurrentMode() == TransferMode.MemoryEntrust) { transferRequired = need_mtrSO.GetMemoryEntrustTransferMaterialRequired(); memoryRequired = need_mtrSO.GetMemoryEntrustMemoryFragmentRequired(); coinRequired = need_mtrSO.GetMemoryEntrustCoinsRequired(); return; } int level = eqp_p_SO != null ? eqp_p_SO.level : 0; transferRequired = need_mtrSO.GetTourEntrustTransferMaterialRequired(level); memoryRequired = need_mtrSO.GetTourEntrustMemoryFragmentRequired(level); coinRequired = need_mtrSO.GetTourEntrustCoinsRequired(level); } private TransferMode GetCurrentMode() { if (chooseType == null) { return TransferMode.MemoryEntrust; } return chooseType.value == (int)TransferMode.TourEntrust ? TransferMode.TourEntrust : TransferMode.MemoryEntrust; } private void EnsureChooseTypeOptions() { if (chooseType == null) { return; } bool rebuild = chooseType.options == null || chooseType.options.Count != 2 || chooseType.options[0].text != "记忆嘱托" || chooseType.options[1].text != "巡演嘱托"; if (!rebuild) { return; } chooseType.ClearOptions(); chooseType.AddOptions(new List { new Dropdown.OptionData("记忆嘱托"), new Dropdown.OptionData("巡演嘱托") }); chooseType.value = 0; chooseType.RefreshShownValue(); } private static equipmentSO.EquipmentSpecialEffect[] CloneEffects(equipmentSO.EquipmentSpecialEffect[] source) { if (source == null || source.Length == 0) { return System.Array.Empty(); } var clone = new equipmentSO.EquipmentSpecialEffect[source.Length]; for (int i = 0; i < source.Length; i++) { equipmentSO.EquipmentSpecialEffect effect = source[i]; clone[i] = effect == null ? null : new equipmentSO.EquipmentSpecialEffect { effectType = effect.effectType, value = effect.value }; } return clone; } private static int GetLevelBucket(int level) { if (level < 0) { return -1; } if (level >= 20) { return 4; } if (level >= 15) { return 3; } if (level >= 10) { return 2; } if (level >= 5) { return 1; } return 0; } private static bool IsPointerOverDropObject(GameObject dropObject, Vector2 screenPoint, Camera eventCamera) { if (dropObject == null || !dropObject.activeInHierarchy) { return false; } RectTransform[] rects = dropObject.GetComponentsInChildren(true); for (int i = 0; i < rects.Length; i++) { RectTransform rect = rects[i]; if (rect == null || rect == dropObject.transform) { continue; } if (RectTransformUtility.RectangleContainsScreenPoint(rect, screenPoint, eventCamera)) { return true; } } RectTransform selfRect = dropObject.transform as RectTransform; return selfRect != null && RectTransformUtility.RectangleContainsScreenPoint(selfRect, screenPoint, eventCamera); } private void ClearSpawnedMaterialItems() { if (mtrParent != null) { for (int i = mtrParent.childCount - 1; i >= 0; i--) { Transform child = mtrParent.GetChild(i); if (child == null) { continue; } #if UNITY_EDITOR if (!Application.isPlaying) { DestroyImmediate(child.gameObject); } else #endif { Destroy(child.gameObject); } } } for (int i = spawnedMaterialItems.Count - 1; i >= 0; i--) { GameObject instance = spawnedMaterialItems[i]; if (instance == null) { continue; } #if UNITY_EDITOR if (!Application.isPlaying) { DestroyImmediate(instance); } else #endif { Destroy(instance); } } spawnedMaterialItems.Clear(); } private void DestroySpawned(ref GameObject instance) { if (instance == null) { return; } #if UNITY_EDITOR if (!Application.isPlaying) { DestroyImmediate(instance); } else #endif { Destroy(instance); } instance = null; } private static void DestroyGeneratedChild(Transform parent, string suffix) { if (parent == null || string.IsNullOrEmpty(suffix)) { return; } for (int i = parent.childCount - 1; i >= 0; i--) { Transform child = parent.GetChild(i); if (child == null || !child.name.EndsWith(suffix)) { continue; } #if UNITY_EDITOR if (!Application.isPlaying) { DestroyImmediate(child.gameObject); } else #endif { Destroy(child.gameObject); } } } private void DestroyPreviewEquipment() { if (previewEquipSO == null) { return; } #if UNITY_EDITOR if (!Application.isPlaying) { DestroyImmediate(previewEquipSO); } else #endif { Destroy(previewEquipSO); } previewEquipSO = null; } private static void PersistEquipment(equipmentSO equipment) { if (equipment == null) { return; } if (Application.isPlaying) { equipmentGenerator.SaveRuntimeEquipmentState(equipment); return; } #if UNITY_EDITOR EditorUtility.SetDirty(equipment); AssetDatabase.SaveAssets(); #endif } private static void DeleteEquipment(equipmentSO equipment) { if (equipment == null) { return; } equipmentGenerator.RemoveRuntimeGeneratedEquipment(equipment); #if UNITY_EDITOR string assetPath = AssetDatabase.GetAssetPath(equipment); if (!string.IsNullOrWhiteSpace(assetPath)) { AssetDatabase.DeleteAsset(assetPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return; } #endif #if UNITY_EDITOR if (!Application.isPlaying) { Object.DestroyImmediate(equipment); } else #endif { Object.Destroy(equipment); } } 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(); } } } }