Files
bansonic_beta_main/Assets/_eqpmtSys/eqpmtDesPrefab.cs
T
2026-07-20 02:22:51 +08:00

851 lines
25 KiB
C#

using System.Collections.Generic;
using Spine.Unity;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class eqpmtDesPrefab : MonoBehaviour
{
[Header("so")]
public equipmentSO eSO;
[Header("display")]
public bool displayBasicAttributes = true;
public bool displaySpecialEffects = true;
public bool displayTypeSameEffects = true;
public bool displayIllusionEffects = true;
public bool displayMaxLevelEffects = true;
[Header("btm")]
public Image eqpmtBtm;
public Sprite[] eqpmtBtmSprites;
[Header("spine particles")]
public GameObject golden_particle;
public GameObject red_particle;
public GameObject pink_particle;
[Header("head")]
public Text eqpmtName;
public Text eqpmtID;
public Text eqpmtLevel;
public Image eqpmtIcon;
public Image eqpmtPbtm;
public Color[] profilebtmColor;
public Text eqpmtDescription;
[Header("body")]
public Text eqpmtType;
public Text baTitle;
public Text baDetail;
public GameObject baSpace;
public Text saTitle;
public Text saDetail;
public GameObject saSpace;
public Text taTitle;
public Text taDetail;
public GameObject taSpace;
public Text iaTitle;
public Text iaDetail;
public GameObject iaSpace;
public Text mlTitle;
public Text mlDetail;
public GameObject eqpSpace;
public GameObject whoEquipping;
public Image equipperProfile;
public Text equipperName;
[Header("objs")]
public GameObject eqpmtSkillPrefab;
public Transform sasParent;
public Transform iasParent;
public Transform mlsParent;
[Header("es description")]
public GameObject esDesPrefab;
public Vector2 esDesPrefabOffset = new Vector2(20f, 30f);
private RectTransform rectTransform;
private Canvas rootCanvas;
private Camera uiCamera;
private bool ignoreCloseUntilClickReleased;
private void Awake()
{
rectTransform = transform as RectTransform;
rootCanvas = GetComponentInParent<Canvas>();
if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay)
{
uiCamera = rootCanvas.worldCamera;
}
}
public void Bind(equipmentSO so)
{
eSO = so;
RefreshView();
}
public void IgnoreCloseUntilClickReleased()
{
ignoreCloseUntilClickReleased = true;
}
private void Update()
{
if (eSO == null || rectTransform == null)
{
return;
}
if (ignoreCloseUntilClickReleased)
{
if (Input.GetMouseButton(0))
{
return;
}
ignoreCloseUntilClickReleased = false;
}
if (!Input.GetMouseButtonDown(0))
{
return;
}
if (RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Input.mousePosition, uiCamera))
{
return;
}
if (IsClickInsidePopupHierarchy(Input.mousePosition))
{
return;
}
Destroy(gameObject);
}
private bool IsClickInsidePopupHierarchy(Vector2 screenPoint)
{
if (EventSystem.current == null)
{
return false;
}
PointerEventData pointer = new PointerEventData(EventSystem.current)
{
position = screenPoint
};
var results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointer, results);
for (int i = 0; i < results.Count; i++)
{
GameObject target = results[i].gameObject;
if (target != null && target.transform.IsChildOf(transform))
{
return true;
}
}
return false;
}
private void RefreshView()
{
ClearSkillPrefabs();
if (eSO == null)
{
ApplySpineParticles(null);
ApplyHeader(string.Empty, null, 0, string.Empty);
ApplyBasicSection(string.Empty, false);
ApplyEffectSection(saTitle, saDetail, saSpace, sasParent, string.Empty, null, false);
ApplyEffectSection(taTitle, taDetail, taSpace, null, string.Empty, null, false);
ApplyEffectSection(iaTitle, iaDetail, iaSpace, iasParent, string.Empty, null, false);
ApplyEffectSection(mlTitle, mlDetail, null, mlsParent, string.Empty, null, false);
ApplyEquipperInfo(null);
return;
}
equipmentSO.EquipmentTierPresentation presentation = ResolveTierPresentation(eSO);
string displayName = eSO.GetDisplayTierName();
if (string.IsNullOrWhiteSpace(displayName))
{
displayName = eSO.name;
}
Sprite displaySprite = presentation != null ? presentation.equipmentSprite : null;
string description = eSO.GetDisplayTierDescription();
ApplySpineParticles(eSO);
ApplyHeader(displayName, displaySprite, GetQualityVisualIndex(eSO), description);
ApplyEquipperInfo(eSO);
string basicText = BuildBasicAttributesText(eSO);
ApplyBasicSection(basicText, displayBasicAttributes && !string.IsNullOrWhiteSpace(basicText));
string specialText = BuildEffectText(eSO.specialEffects, out int saSkillId);
bool hasSpecialSkill = HasSkillEffect(eSO.specialEffects);
ApplyEffectSection(
saTitle,
saDetail,
saSpace,
sasParent,
specialText,
hasSpecialSkill && saSkillId > 0 ? new[] { saSkillId } : null,
displaySpecialEffects && HasSectionContent(eSO.specialEffects, saSkillId));
string typeSameText = BuildEffectText(eSO.typeSameEffects, out _);
ApplyEffectSection(
taTitle,
taDetail,
taSpace,
null,
typeSameText,
null,
displayTypeSameEffects && HasSectionContent(eSO.typeSameEffects, 0));
string illusionText = BuildEffectText(eSO.illusionEffects, out int iaSkillId);
bool isTourPreview = IsTourPreviewEquipment(eSO);
if (isTourPreview)
{
illusionText = string.IsNullOrWhiteSpace(illusionText)
? "<color=#9DFF84>1个随机巡演特效</color>"
: $"{illusionText}\n<color=#9DFF84>1个随机巡演特效</color>";
}
bool hasIllusionSkill = iaSkillId > 0;
ApplyEffectSection(
iaTitle,
iaDetail,
iaSpace,
iasParent,
illusionText,
hasIllusionSkill ? new[] { iaSkillId } : null,
displayIllusionEffects && (isTourPreview || HasSectionContent(eSO.illusionEffects, iaSkillId)));
string maxLevelText = BuildEffectText(eSO.maxLevelEffects, out _);
ApplyEffectSection(
mlTitle,
mlDetail,
null,
mlsParent,
maxLevelText,
null,
displayMaxLevelEffects && HasSectionContent(eSO.maxLevelEffects, 0));
}
private void ApplySpineParticles(equipmentSO equipment)
{
bool showGolden = false;
bool showRed = false;
bool showPink = false;
if (equipment != null)
{
int level = Mathf.Max(0, equipment.level);
bool hasFinalDreamEffect = equipment.HasFinalDreamEffect();
if (level >= 20 && hasFinalDreamEffect)
{
showPink = true;
}
else if (level >= 15)
{
showRed = true;
}
else if (level >= 10)
{
showGolden = true;
}
}
if (golden_particle != null)
{
golden_particle.SetActive(showGolden);
}
if (red_particle != null)
{
red_particle.SetActive(showRed);
}
if (pink_particle != null)
{
pink_particle.SetActive(showPink);
}
}
private static void ConfigureSpineParticleRenderers(GameObject target)
{
if (target == null)
{
return;
}
SkeletonGraphic[] skeletonGraphics = target.GetComponentsInChildren<SkeletonGraphic>(true);
for (int i = 0; i < skeletonGraphics.Length; i++)
{
SkeletonGraphic skeletonGraphic = skeletonGraphics[i];
if (skeletonGraphic == null || skeletonGraphic.allowMultipleCanvasRenderers)
{
continue;
}
skeletonGraphic.allowMultipleCanvasRenderers = true;
skeletonGraphic.Initialize(true);
skeletonGraphic.SetMaterialDirty();
skeletonGraphic.SetVerticesDirty();
}
}
private void ApplyEquipperInfo(equipmentSO equipment)
{
AllyHero_SO equipper = FindEquipper(equipment);
bool hasEquipper = equipper != null;
if (whoEquipping != null)
{
whoEquipping.SetActive(hasEquipper);
}
if (eqpSpace != null)
{
eqpSpace.SetActive(hasEquipper);
}
if (equipperProfile != null)
{
equipperProfile.sprite = hasEquipper ? equipper.ally_hero_squareProfile : null;
equipperProfile.color = hasEquipper && equipper.ally_hero_squareProfile != null ? Color.white : new Color(1f, 1f, 1f, 0f);
}
if (equipperName != null)
{
equipperName.text = hasEquipper ? equipper.ally_heroName : string.Empty;
}
}
private AllyHero_SO FindEquipper(equipmentSO equipment)
{
if (equipment == null)
{
return null;
}
AllyHero_SO[] heroes = LoadHeroAssets();
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null)
{
continue;
}
hero.LoadEquippedEquipmentFromLocal();
equipmentSO equipped = hero.GetEquippedEquipmentResolved();
if (equipped == equipment || (!string.IsNullOrWhiteSpace(equipped?.name) && equipped.name == equipment.name))
{
return hero;
}
}
return null;
}
private AllyHero_SO[] LoadHeroAssets()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
string[] guids = AssetDatabase.FindAssets("t:AllyHero_SO", new[] { "Assets/Resources/so/ally" });
var heroes = new List<AllyHero_SO>(guids.Length);
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
AllyHero_SO hero = AssetDatabase.LoadAssetAtPath<AllyHero_SO>(path);
if (hero != null)
{
heroes.Add(hero);
}
}
return heroes.ToArray();
}
#endif
return RuntimeResourcesCache.LoadAllAllyHeroes();
}
private equipmentSO.EquipmentTierPresentation ResolveTierPresentation(equipmentSO equipment)
{
if (equipment == null)
{
return null;
}
if (equipment.tierNameConfig != null &&
equipment.tierNameConfig.TryGetStagePresentation(equipment.skillType, equipment.GetVisualTierStageIndex(), out equipmentTierNameConfigSO.StagePresentation stage) &&
stage != null)
{
return new equipmentSO.EquipmentTierPresentation
{
tierName = stage.tierName,
equipmentSprite = stage.equipmentSprite,
tierDescription = stage.tierDescription
};
}
return equipment.GetVisualTierPresentation();
}
private void ApplyHeader(string displayName, Sprite displaySprite, int qualityIndex, string description)
{
if (eqpmtName != null)
{
eqpmtName.text = displayName;
eqpmtName.color = GetProfileColor(qualityIndex);
}
if (eqpmtID != null)
{
eqpmtID.text = BuildDisplayEquipmentId(eSO);
}
if (eqpmtLevel != null)
{
eqpmtLevel.text = eSO != null ? Mathf.Max(0, eSO.level).ToString() : string.Empty;
}
if (eqpmtDescription != null)
{
eqpmtDescription.text = description;
}
if (eqpmtType != null)
{
eqpmtType.text = eSO != null ? eSO.skillType.ToString() : string.Empty;
}
if (eqpmtIcon != null)
{
eqpmtIcon.sprite = displaySprite;
eqpmtIcon.enabled = displaySprite != null;
}
if (eqpmtBtm != null)
{
eqpmtBtm.sprite = GetBottomSprite(qualityIndex);
eqpmtBtm.enabled = eqpmtBtm.sprite != null;
}
if (eqpmtPbtm != null)
{
eqpmtPbtm.color = GetProfileColor(qualityIndex);
}
}
private void ApplyBasicSection(string text, bool active)
{
SetGroupActive(baTitle, baDetail, baSpace, null, active);
if (baDetail != null)
{
baDetail.supportRichText = true;
baDetail.text = text;
baDetail.gameObject.SetActive(active && !string.IsNullOrWhiteSpace(text));
}
}
private void ApplyEffectSection(
Text title,
Text detail,
GameObject space,
Transform skillParent,
string text,
int[] skillIds,
bool active)
{
bool hasText = !string.IsNullOrWhiteSpace(text);
bool hasSkills = skillParent != null && skillIds != null && skillIds.Length > 0;
bool showGroup = active && (hasText || hasSkills);
SetGroupActive(title, detail, space, null, showGroup);
if (detail != null)
{
detail.supportRichText = true;
detail.text = text;
detail.gameObject.SetActive(showGroup && hasText);
}
bool showSkillParent = showGroup && hasSkills;
if (skillParent != null)
{
skillParent.gameObject.SetActive(showSkillParent);
}
if (!showSkillParent)
{
return;
}
for (int i = 0; i < skillIds.Length; i++)
{
SpawnSkillPrefab(skillParent, skillIds[i]);
}
}
private void SpawnSkillPrefab(Transform parent, int skillId)
{
if (eqpmtSkillPrefab == null || parent == null || skillId <= 0)
{
return;
}
GameObject instance = Instantiate(eqpmtSkillPrefab, parent);
instance.name = $"eqpmtSkill_{skillId}";
SkillGroup skillGroup = EquipmentSkillGroupLibrary.ResolveSkillGroup(skillId);
string displayName = skillGroup != null && !string.IsNullOrWhiteSpace(skillGroup.groupName)
? skillGroup.groupName
: $"Skill {skillId}";
Sprite displayIcon = skillGroup != null ? skillGroup.groupIcon : null;
esPrefab legacyItem = instance.GetComponent<esPrefab>();
if (legacyItem != null)
{
legacyItem.BindSkillGroup(skillId, displayName, displayIcon, esDesPrefab, transform as RectTransform, esDesPrefabOffset);
return;
}
eqpmtSkillPrefabItem item = instance.GetComponent<eqpmtSkillPrefabItem>();
if (item != null)
{
item.Bind(skillId);
return;
}
Text[] texts = instance.GetComponentsInChildren<Text>(true);
for (int i = 0; i < texts.Length; i++)
{
if (texts[i] != null)
{
texts[i].text = displayName;
break;
}
}
}
private void ClearSkillPrefabs()
{
ClearChildren(sasParent);
ClearChildren(iasParent);
ClearChildren(mlsParent);
}
private static void ClearChildren(Transform parent)
{
if (parent == null)
{
return;
}
for (int i = parent.childCount - 1; i >= 0; i--)
{
Transform child = parent.GetChild(i);
if (Application.isPlaying)
{
Destroy(child.gameObject);
}
else
{
DestroyImmediate(child.gameObject);
}
}
}
private static void SetGroupActive(Text title, Text detail, GameObject space, Transform skillParent, bool active)
{
if (title != null)
{
title.gameObject.SetActive(active);
}
if (detail != null)
{
detail.gameObject.SetActive(active);
}
if (space != null)
{
space.SetActive(active);
}
if (skillParent != null)
{
skillParent.gameObject.SetActive(active);
}
}
private bool HasSectionContent(equipmentSO.EquipmentSpecialEffect[] effects, int skillId)
{
if (skillId > 0)
{
return true;
}
if (effects == null)
{
return false;
}
for (int i = 0; i < effects.Length; i++)
{
if (effects[i] != null &&
effects[i].effectType != equipmentSO.EquipmentSpecialEffectType.Skill &&
!Mathf.Approximately(effects[i].value, 0f))
{
return true;
}
}
return false;
}
private static bool HasSkillEffect(equipmentSO.EquipmentSpecialEffect[] effects)
{
if (effects == null)
{
return false;
}
for (int i = 0; i < effects.Length; i++)
{
if (effects[i] != null && effects[i].effectType == equipmentSO.EquipmentSpecialEffectType.Skill)
{
return true;
}
}
return false;
}
private string BuildBasicAttributesText(equipmentSO equipment)
{
if (equipment == null)
{
return string.Empty;
}
var lines = new List<string>();
AppendBasicLine(lines, "最大生命", equipment.maxHp, equipmentSO.EquipmentSpecialEffectType.MaxHp);
AppendBasicLine(lines, "攻击力", equipment.attack, equipmentSO.EquipmentSpecialEffectType.Attack);
AppendBasicLine(lines, "最大法力", equipment.maxMana, equipmentSO.EquipmentSpecialEffectType.MaxMana);
AppendBasicLine(lines, "伤害减免", equipment.damageResistance, equipmentSO.EquipmentSpecialEffectType.DamageResistance);
AppendBasicLine(lines, "得分效率", equipment.scoreEfficiency, equipmentSO.EquipmentSpecialEffectType.ScoreEfficiency);
return string.Join("\n", lines);
}
private void AppendBasicLine(List<string> lines, string label, equipmentSO.EquipmentStatTuning tuning, equipmentSO.EquipmentSpecialEffectType effectType)
{
if (lines == null || tuning == null)
{
return;
}
float baseValue = tuning.basicGain;
if (Mathf.Approximately(baseValue, 0f))
{
return;
}
float levelGain = eSO != null ? Mathf.Max(0, eSO.level) * tuning.cultivationInterval : 0f;
float finalDreamGain = GetMaxLevelBoost(effectType);
float total = baseValue + levelGain + finalDreamGain;
if (eSO == null || (Mathf.Max(0, eSO.level) <= 0 && Mathf.Approximately(finalDreamGain, 0f)))
{
lines.Add($"{label}{FormatTotalPercent(baseValue + finalDreamGain)}");
return;
}
string componentText = $"<color=#ADD8E6>{FormatComponentPercent(baseValue)}</color>";
if (!Mathf.Approximately(levelGain, 0f))
{
componentText += $"+<color=#F0E68C>{FormatComponentPercent(levelGain)}</color>";
}
if (!Mathf.Approximately(finalDreamGain, 0f))
{
componentText += $"+<color=#FF67A4>{FormatComponentPercent(finalDreamGain)}</color>";
}
lines.Add($"{label}{FormatTotalPercent(total)} ({componentText})");
}
private string BuildEffectText(equipmentSO.EquipmentSpecialEffect[] effects, out int skillId)
{
skillId = 0;
if (effects == eSO?.specialEffects)
{
skillId = eSO != null ? eSO.sa_skillID : 0;
}
else if (effects == eSO?.illusionEffects)
{
skillId = eSO != null ? eSO.ia_skillID : 0;
}
if (effects == null || effects.Length == 0)
{
return string.Empty;
}
bool isTypeSameEffectSection = effects == eSO?.typeSameEffects;
bool isMaxLevelEffectSection = effects == eSO?.maxLevelEffects;
var lines = new List<string>();
for (int i = 0; i < effects.Length; i++)
{
equipmentSO.EquipmentSpecialEffect effect = effects[i];
if (effect == null)
{
continue;
}
if (effect.effectType == equipmentSO.EquipmentSpecialEffectType.Skill)
{
continue;
}
if (Mathf.Approximately(effect.value, 0f))
{
continue;
}
string valueText = (isTypeSameEffectSection || isMaxLevelEffectSection)
? FormatSignedPercent(effect.value)
: FormatSignedValue(effect.value);
lines.Add($"{GetEffectDisplayName(effect.effectType)}{valueText}");
}
return string.Join("\n", lines);
}
private float GetMaxLevelBoost(equipmentSO.EquipmentSpecialEffectType effectType)
{
if (eSO == null || eSO.maxLevelEffects == null)
{
return 0f;
}
for (int i = 0; i < eSO.maxLevelEffects.Length; i++)
{
equipmentSO.EquipmentSpecialEffect effect = eSO.maxLevelEffects[i];
if (effect == null || effect.effectType != effectType)
{
continue;
}
return effect.value;
}
return 0f;
}
private Sprite GetBottomSprite(int qualityIndex)
{
if (eqpmtBtmSprites == null || eqpmtBtmSprites.Length == 0)
{
return null;
}
int safeIndex = Mathf.Clamp(qualityIndex, 0, eqpmtBtmSprites.Length - 1);
return eqpmtBtmSprites[safeIndex];
}
private Color GetProfileColor(int qualityIndex)
{
if (profilebtmColor == null || profilebtmColor.Length == 0)
{
return Color.white;
}
int safeIndex = Mathf.Clamp(qualityIndex, 0, profilebtmColor.Length - 1);
return profilebtmColor[safeIndex];
}
private static int GetQualityVisualIndex(equipmentSO equipment)
{
if (equipment == null)
{
return 0;
}
return equipment.GetVisualQualityColorIndex();
}
private static string GetEffectDisplayName(equipmentSO.EquipmentSpecialEffectType effectType)
{
switch (effectType)
{
case equipmentSO.EquipmentSpecialEffectType.MaxHp: return "最大生命";
case equipmentSO.EquipmentSpecialEffectType.Attack: return "攻击力";
case equipmentSO.EquipmentSpecialEffectType.MaxMana: return "最大法力";
case equipmentSO.EquipmentSpecialEffectType.DamageResistance: return "伤害减免";
case equipmentSO.EquipmentSpecialEffectType.ScoreEfficiency: return "得分效率";
case equipmentSO.EquipmentSpecialEffectType.HitManaRecovery: return "hit法力恢复";
case equipmentSO.EquipmentSpecialEffectType.HitDamageMultiplier: return "hit伤害倍率";
case equipmentSO.EquipmentSpecialEffectType.HpLoseBase: return "基本生命损失";
case equipmentSO.EquipmentSpecialEffectType.Skill: return "技能";
default: return effectType.ToString();
}
}
private static string FormatTotalPercent(float value)
{
float percent = value * 100f;
return $"{(percent >= 0f ? "+" : string.Empty)}{percent:0.0}%";
}
private static string FormatComponentPercent(float value)
{
float percent = value * 100f;
return $"{percent:0.0}%";
}
private static string FormatSignedValue(float value)
{
return $"{(value >= 0f ? "+" : string.Empty)}{value:0.####}";
}
private static string FormatSignedPercent(float value)
{
float percent = value * 100f;
return $"{(percent >= 0f ? "+" : string.Empty)}{percent:0.##}%";
}
private static string BuildDisplayEquipmentId(equipmentSO equipment)
{
if (equipment == null || string.IsNullOrWhiteSpace(equipment.name))
{
return string.Empty;
}
string raw = equipment.name;
bool isPreview = raw.Contains("(Clone)") || raw.Contains("(TourPreview)");
raw = raw.Replace("(Clone)", string.Empty).Replace("(TourPreview)", string.Empty).Trim();
if (raw.StartsWith("type"))
{
raw = raw.Substring(4);
}
return isPreview ? $"{raw}(Preview)" : raw;
}
private static bool IsTourPreviewEquipment(equipmentSO equipment)
{
return equipment != null &&
!string.IsNullOrWhiteSpace(equipment.name) &&
equipment.name.Contains("(TourPreview)");
}
}