From af13bd5c47bb58aa79b3c02b2a5f17d4f7b1656c Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Tue, 27 Jan 2026 17:39:03 +0700 Subject: [PATCH] Add DlgCharacter ui logic --- .../Scripts/UI/Dialogs/DlgCharacter.cs | 550 ++++++++++++++++++ .../Scripts/UI/Dialogs/DlgCharacter.cs.meta | 2 + 2 files changed, 552 insertions(+) create mode 100644 Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs create mode 100644 Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs.meta diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs new file mode 100644 index 0000000000..f3952a227f --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs @@ -0,0 +1,550 @@ +// Port of C++ CDlgCharacter (DlgCharacter.cpp / DlgCharacter.h) +// Character information panel: stats, attributes, equipment bonuses, attribute point allocation. + +using System; +using System.Collections.Generic; +using System.Text; +using BrewMonster.Network; +using BrewMonster.Scripts; +using BrewMonster.Scripts.Managers; +using PerfectWorld.Scripts.Managers; +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.EventSystems; +using TMPro; + +namespace BrewMonster.UI +{ + public class DlgCharacter : AUIDialog + { + private const int INTERAL_LONG = 618; + private const int INTERAL_SHORT = 100; + + #region UI References (same names as C++) + + [Header("Basic Info")] + [SerializeField] private TextMeshProUGUI m_pTxt_CharName; + [SerializeField] private TextMeshProUGUI m_pTxt_CharLevel; + [SerializeField] private TextMeshProUGUI m_pTxt_Profession; + [SerializeField] private TextMeshProUGUI m_pTxt_Faction; + [SerializeField] private TextMeshProUGUI m_pTxt_Partner; + [SerializeField] private TextMeshProUGUI m_pTxt_ExpCurrent; + [SerializeField] private TextMeshProUGUI m_pTxt_ExpRequire; + [SerializeField] private TextMeshProUGUI m_pTxt_Distinction; + [SerializeField] private TextMeshProUGUI m_pTxt_xz; + + [Header("Resources")] + [SerializeField] private TextMeshProUGUI m_pTxt_HP; + [SerializeField] private TextMeshProUGUI m_pTxt_MP; + [SerializeField] private TextMeshProUGUI m_pTxt_SP; + [SerializeField] private TextMeshProUGUI m_pTxt_Point; + + [Header("Attributes")] + [SerializeField] private TextMeshProUGUI m_pTxt_Str; + [SerializeField] private TextMeshProUGUI m_pTxt_Agi; + [SerializeField] private TextMeshProUGUI m_pTxt_Vit; + [SerializeField] private TextMeshProUGUI m_pTxt_Int; + + [Header("Combat Stats")] + [SerializeField] private TextMeshProUGUI m_pTxt_Attack; + [SerializeField] private TextMeshProUGUI m_pTxt_AtkSpeed; + [SerializeField] private TextMeshProUGUI m_pTxt_Definition; + [SerializeField] private TextMeshProUGUI m_pTxt_Evade; + [SerializeField] private TextMeshProUGUI m_pTxt_MoveSpeed; + [SerializeField] private TextMeshProUGUI m_pTxt_MgcAttack; + [SerializeField] private TextMeshProUGUI m_pTxt_Critical; + [SerializeField] private TextMeshProUGUI m_pTxt_PhyDefense; + [SerializeField] private TextMeshProUGUI m_pTxt_MgcDefense; + [SerializeField] private TextMeshProUGUI m_pTxt_AttackLevel; + [SerializeField] private TextMeshProUGUI m_pTxt_DefenseLevel; + [SerializeField] private TextMeshProUGUI m_pTxt_CrtPower; + [SerializeField] private TextMeshProUGUI m_pTxt_SoulPower; + + [Header("Hints / Additional")] + [SerializeField] private TextMeshProUGUI m_pNew_PhyDefense; + [SerializeField] private TextMeshProUGUI m_pNew_MgcDefense; + [SerializeField] private TextMeshProUGUI m_pNew_AttackLevel; + [SerializeField] private TextMeshProUGUI m_pNew_DefenseLevel; + [SerializeField] private TextMeshProUGUI m_pTxt_Force; + [SerializeField] private TextMeshProUGUI m_pTxt_Title; + [SerializeField] private TextMeshProUGUI m_pTxt_Realm; + [SerializeField] private TextMeshProUGUI m_pTxt_Vigour; + [SerializeField] private TextMeshProUGUI m_pTxt_Stealth; + [SerializeField] private TextMeshProUGUI m_pTxt_Antistealth; + [SerializeField] private TextMeshProUGUI m_pTxt_Penetration; + [SerializeField] private TextMeshProUGUI m_pTxt_Resilience; + [SerializeField] private TextMeshProUGUI m_pNew_Vigour; + [SerializeField] private TextMeshProUGUI m_pNew_SoulPower; + [SerializeField] private TextMeshProUGUI m_pNew_Penetration; + [SerializeField] private TextMeshProUGUI m_pNew_Resilience; + + [Header("Character Portrait")] + [SerializeField] private RawImage m_pImg_Char; + [Tooltip("Optional. Pivot transform to rotate (Y) when dragging on portrait. E.g. character model or camera rig.")] + [SerializeField] private Transform m_pPortraitPivot; + + [Header("Realm Progress")] + [SerializeField] private Slider m_pPro_RealmExp; + + [Header("Buttons")] + [SerializeField] private Button m_pBtn_AddStr; + [SerializeField] private Button m_pBtn_AddAgi; + [SerializeField] private Button m_pBtn_AddVit; + [SerializeField] private Button m_pBtn_AddInt; + [SerializeField] private Button m_pBtn_MinusStr; + [SerializeField] private Button m_pBtn_MinusAgi; + [SerializeField] private Button m_pBtn_MinusVit; + [SerializeField] private Button m_pBtn_MinusInt; + [SerializeField] private Button m_pBtn_Confirm; + [SerializeField] private Button m_pBtn_Reset; + [SerializeField] private Button m_pBtn_Cancel; + [SerializeField] private Button m_pBtn_Force; + [SerializeField] private Button m_pBtn_TitleList; + [SerializeField] private Button m_pBtn_Book; + + #endregion + + #region State (same as C++) + + private int m_nStatusPtUsed; + private ROLEEXTPROP_BASE m_repBase; + private int m_nMouseLastX; + private int m_nMouseOffset; + private int m_nMouseOffsetThis; + private float m_dwStartTime; + private float m_dwLastTime; + private int m_iIntervalTime; + private Button m_pButtonPress; + private bool m_bAdd; + + private static readonly Color ColorBonus = new Color(0f, 1f, 0f); + private static readonly Color ColorNormal = Color.white; + + private string m_cachedHostName = ""; + + #endregion + + public override void OnEnable() + { + base.OnEnable(); + EventBus.Subscribe(OnInfoHostPlayer); + } + + public override void OnDisable() + { + EventBus.Unsubscribe(OnInfoHostPlayer); + base.OnDisable(); + } + + private void OnInfoHostPlayer(CECHostPlayer.InfoHostPlayer e) + { + m_cachedHostName = e.NameHostPlayer ?? ""; + } + + public override void Awake() + { + base.Awake(); + SetName("Win_Character"); + m_nMouseOffset = 0; + m_nMouseOffsetThis = 0; + m_iIntervalTime = INTERAL_LONG; + m_dwLastTime = 0; + m_pButtonPress = null; + ResetPoints(); + } + + public override void Start() + { + base.Start(); + WireButtons(); + WirePortraitDrag(); + } + + private void WirePortraitDrag() + { + if (m_pImg_Char == null) return; + var go = m_pImg_Char.gameObject; + var receiver = go.GetComponent(); + if (receiver == null) receiver = go.AddComponent(); + receiver.Init(this); + } + + /// Called by portrait drag receiver. deltaX in screen space; rotation clamped to [-70,70] degrees. + public void OnPortraitDragDelta(float deltaX) + { + m_nMouseOffset += (int)deltaX; + m_nMouseOffset = Mathf.Clamp(m_nMouseOffset, -70, 70); + m_nMouseOffsetThis = m_nMouseOffset; + if (m_pPortraitPivot != null) + m_pPortraitPivot.localRotation = Quaternion.Euler(0f, m_nMouseOffset, 0f); + } + + public override void Show(bool value) + { + m_bShow = value; + base.Show(value); + } + + private void WireButtons() + { + if (m_pBtn_AddStr != null) m_pBtn_AddStr.onClick.AddListener(() => OnAddAttr(m_pBtn_AddStr, 0)); // Str + if (m_pBtn_AddAgi != null) m_pBtn_AddAgi.onClick.AddListener(() => OnAddAttr(m_pBtn_AddAgi, 1)); + if (m_pBtn_AddVit != null) m_pBtn_AddVit.onClick.AddListener(() => OnAddAttr(m_pBtn_AddVit, 2)); // Vit maps to 3 in C++ iFourPro + if (m_pBtn_AddInt != null) m_pBtn_AddInt.onClick.AddListener(() => OnAddAttr(m_pBtn_AddInt, 3)); // Int/Energy + if (m_pBtn_MinusStr != null) m_pBtn_MinusStr.onClick.AddListener(OnMinusStr); + if (m_pBtn_MinusAgi != null) m_pBtn_MinusAgi.onClick.AddListener(OnMinusAgi); + if (m_pBtn_MinusVit != null) m_pBtn_MinusVit.onClick.AddListener(OnMinusVit); + if (m_pBtn_MinusInt != null) m_pBtn_MinusInt.onClick.AddListener(OnMinusInt); + if (m_pBtn_Confirm != null) m_pBtn_Confirm.onClick.AddListener(OnCommand_confirm); + if (m_pBtn_Reset != null) m_pBtn_Reset.onClick.AddListener(OnCommand_reset); + if (m_pBtn_Cancel != null) m_pBtn_Cancel.onClick.AddListener(OnCommand_CANCEL); + if (m_pBtn_Force != null) m_pBtn_Force.onClick.AddListener(OnCommand_force); + if (m_pBtn_TitleList != null) m_pBtn_TitleList.onClick.AddListener(OnCommand_Title); + if (m_pBtn_Book != null) m_pBtn_Book.onClick.AddListener(OnCommand_ReincarnationBook); + } + + public override bool Render() + { + RefreshHostDetails(); + return base.Render(); + } + + public override void Update() + { + OnTick(); + base.Update(); + } + + private void OnTick() + { + if (m_pButtonPress == null) return; + if (!m_pButtonPress.gameObject.activeInHierarchy) { m_pButtonPress = null; return; } + float now = Time.realtimeSinceStartup * 1000f; + if (now - m_dwLastTime >= m_iIntervalTime) + { + if (m_bAdd) + PropertyAdd(m_pButtonPress); + else + PropertyMinus(m_pButtonPress); + m_dwLastTime += m_iIntervalTime; + if (m_iIntervalTime == INTERAL_LONG) + m_iIntervalTime = INTERAL_SHORT; + } + } + + private void OnAddAttr(Button btn, int kind) + { + PropertyAdd(btn); + // Don't start continuous loop - only increment once per click + m_pButtonPress = null; + } + + private void OnMinusStr() { PropertyMinus(m_pBtn_MinusStr); } + private void OnMinusAgi() { PropertyMinus(m_pBtn_MinusAgi); } + private void OnMinusVit() { PropertyMinus(m_pBtn_MinusVit); } + private void OnMinusInt() { PropertyMinus(m_pBtn_MinusInt); } + + public override void ResetPoints() + { + m_nStatusPtUsed = 0; + m_repBase = new ROLEEXTPROP_BASE(false); + m_repBase.vitality = 0; + m_repBase.energy = 0; + m_repBase.strength = 0; + m_repBase.agility = 0; + } + + private void PropertyAdd(Button btn) + { + CECHostPlayer host = GetHostPlayer(); + if (host == null) return; + ROLEBASICPROP rbp = host.GetBasicProps(); + if (m_nStatusPtUsed >= rbp.iStatusPt) return; + if (btn == m_pBtn_AddStr) { m_repBase.strength++; m_nStatusPtUsed++; } + else if (btn == m_pBtn_AddAgi) { m_repBase.agility++; m_nStatusPtUsed++; } + else if (btn == m_pBtn_AddVit) { m_repBase.vitality++; m_nStatusPtUsed++; } + else if (btn == m_pBtn_AddInt) { m_repBase.energy++; m_nStatusPtUsed++; } + } + + private void PropertyMinus(Button btn) + { + if (btn == m_pBtn_MinusStr && m_repBase.strength > 0) { m_repBase.strength--; m_nStatusPtUsed--; } + else if (btn == m_pBtn_MinusAgi && m_repBase.agility > 0) { m_repBase.agility--; m_nStatusPtUsed--; } + else if (btn == m_pBtn_MinusVit && m_repBase.vitality > 0) { m_repBase.vitality--; m_nStatusPtUsed--; } + else if (btn == m_pBtn_MinusInt && m_repBase.energy > 0) { m_repBase.energy--; m_nStatusPtUsed--; } + } + + private void OnCommand_confirm() + { + UnityGameSession.c2s_CmdSetStatusPts( + m_repBase.vitality, m_repBase.energy, + m_repBase.strength, m_repBase.agility); + ResetPoints(); + // Request fresh ext props so host player gets updated values from server; UI will refresh when response arrives + UnityGameSession.c2s_SendCmdGetExtProps(); + } + + private void OnCommand_reset() + { + ResetPoints(); + } + + private void OnCommand_CANCEL() + { + Show(false); + } + + private void OnCommand_force() + { + var pForce = m_pAUIManager?.GetDialog("Win_Force"); + if (pForce != null) pForce.Show(!pForce.IsShow()); + } + + private void OnCommand_Title() + { + var pDlg = GetGameUIMan()?.GetDialog("Win_TitleList"); + if (pDlg != null) pDlg.Show(!pDlg.IsShow()); + } + + private void OnCommand_ReincarnationBook() + { + CECHostPlayer host = GetHostPlayer(); + if (host == null) return; + var pDlg = m_pAUIManager?.GetDialog("Win_ReincarnationBook"); + if (pDlg != null && host.GetReincarnationCount() > 0) pDlg.Show(!pDlg.IsShow()); + } + + private void RefreshHostDetails() + { + if (!gameObject.activeSelf) return; + CECHostPlayer host = GetHostPlayer(); + if (host == null) return; + + ROLEBASICPROP rbp = host.GetBasicProps(); + ROLEEXTPROP rep = host.GetExtendProps(); + var gameRun = EC_Game.GetGameRun(); + if (gameRun == null) return; + + string charName = host.GetName(); + if (string.IsNullOrEmpty(charName)) charName = m_cachedHostName; + if (string.IsNullOrEmpty(charName)) + { + var ri = UnityGameSession.Instance?.GetRoleInfo(); + if (ri?.name != null && ri.name.ByteArray != null && ri.name.ByteArray.Length > 0) + charName = Encoding.Unicode.GetString(ri.name.ByteArray); + } + SetText(m_pTxt_CharName, charName ?? ""); + SetText(m_pTxt_CharLevel, rbp.iLevel.ToString()); + SetText(m_pTxt_Profession, gameRun.GetProfName(host.GetProfession())); + + int idFaction = host.GetFactionID(); + string factionName = idFaction <= 0 ? GetStringFromTable(251) : ""; + var factionMan = EC_Game.GetFactionMan(); + if (idFaction > 0 && factionMan != null) + { + var fi = factionMan.GetFaction((uint)idFaction, false); + if (fi != null) factionName = fi.m_szName ?? ""; + } + SetText(m_pTxt_Faction, factionName); + + int spouse = 0; // TODO: host.GetSpouse() when exposed; use gameRun.GetPlayerName(spouse, false) when that API exists + if (spouse != 0) + { + string spouseName = ""; // TODO: gameRun.GetPlayerName(spouse, false) + SetText(m_pTxt_Partner, spouseName ?? ""); + } + else + SetText(m_pTxt_Partner, GetStringFromTable(786)); + + int[] iFourPro = CalcEquipmentBonus(host); + + SetText(m_pTxt_ExpCurrent, rbp.iExp.ToString()); + SetText(m_pTxt_ExpRequire, host.GetLevelUpExp(rbp.iLevel).ToString()); + int reputation = 0; // TODO: host.GetReputation() when exposed + SetText(m_pTxt_Distinction, reputation.ToString()); + SetText(m_pTxt_xz, GetGameUIMan()?.GetStringFromTable(1001 + rbp.iLevel2) ?? ""); + + SetText(m_pTxt_HP, $"{rbp.iCurHP}/{rep.bs.max_hp}"); + SetText(m_pTxt_MP, $"{rbp.iCurMP}/{rep.bs.max_mp}"); + SetText(m_pTxt_SP, rbp.iSP.ToString()); + SetText(m_pTxt_Point, (rbp.iStatusPt - m_nStatusPtUsed).ToString()); + + int strVal = rep.bs.strength + m_repBase.strength; + SetText(m_pTxt_Str, strVal.ToString()); + SetColor(m_pTxt_Str, iFourPro[0] != 0 ? ColorBonus : ColorNormal); + + int agiVal = rep.bs.agility + m_repBase.agility; + SetText(m_pTxt_Agi, agiVal.ToString()); + SetColor(m_pTxt_Agi, iFourPro[1] != 0 ? ColorBonus : ColorNormal); + + int vitVal = rep.bs.vitality + m_repBase.vitality; + SetText(m_pTxt_Vit, vitVal.ToString()); + SetColor(m_pTxt_Vit, iFourPro[3] != 0 ? ColorBonus : ColorNormal); + + int energyVal = rep.bs.energy + m_repBase.energy; + SetText(m_pTxt_Int, energyVal.ToString()); + SetColor(m_pTxt_Int, iFourPro[2] != 0 ? ColorBonus : ColorNormal); + + SetText(m_pTxt_Attack, $"{rep.ak.DamageLow}-{rep.ak.DamageHigh}"); + string atkSpeedUnit = GetGameUIMan()?.GetStringFromTable(279) ?? "/s"; + float atkSpeed = rep.ak.AttackSpeed != 0 ? 1f / (rep.ak.AttackSpeed * 0.05f) : 0f; + SetText(m_pTxt_AtkSpeed, $"{atkSpeed:F2} {atkSpeedUnit}"); + SetText(m_pTxt_Definition, rep.ak.Attack.ToString()); + SetText(m_pTxt_Evade, rep.df.armor.ToString()); + string moveUnit = GetGameUIMan()?.GetStringFromTable(280) ?? "m/s"; + SetText(m_pTxt_MoveSpeed, $"{rep.mv.run_speed:F1} {moveUnit}"); + SetText(m_pTxt_MgcAttack, $"{rep.ak.DamageMagicLow}-{rep.ak.DamageMagicHigh}"); + + SetText(m_pTxt_Critical, $"{rbp.iCritRate}%"); + int nLevel = rbp.iLevel; + float fReduce = DefenceToPercent(rep.df.defense, nLevel); + SetText(m_pTxt_PhyDefense, rep.df.defense.ToString()); + SetText(m_pNew_PhyDefense, string.Format(GetGameUIMan()?.GetStringFromTable(490) ?? "{0} {1}", nLevel, fReduce)); + + if (m_pTxt_MgcDefense != null || m_pNew_MgcDefense != null) + { + int nSum = 0; + var parts = new System.Collections.Generic.List(); + for (int i = 0; i < GameConstants.NUM_MAGICCLASS; i++) + { + nSum += rep.df.resistance[i]; + float r = DefenceToPercent(rep.df.resistance[i], nLevel); + parts.Add(string.Format(GetGameUIMan()?.GetStringFromTable(491 + i) ?? "", rep.df.resistance[i], nLevel, r)); + } + if (m_pTxt_MgcDefense != null) SetText(m_pTxt_MgcDefense, (nSum / GameConstants.NUM_MAGICCLASS).ToString()); + if (m_pNew_MgcDefense != null) SetText(m_pNew_MgcDefense, string.Join("\n", parts)); + } + + SetText(m_pTxt_AttackLevel, rbp.iAtkDegree.ToString()); + SetText(m_pTxt_DefenseLevel, rbp.iDefDegree.ToString()); + SetText(m_pTxt_CrtPower, $"{rbp.iCritDamageBonus + 200}%"); + + int soulPower = 0; // TODO: host.GetSoulPower() when exposed + SetText(m_pTxt_SoulPower, soulPower >= 0 ? soulPower.ToString() : "-"); + string soulHint = GetStringFromTable(8135); + if (soulPower > 0 && host.GetProfession() == (int)PROFESSION.PROF_MONK) + { + soulHint = GetStringFromTable(8136) + "\n" + + string.Format(GetStringFromTable(8130), soulPower) + "\n" + + string.Format(GetStringFromTable(8131), (int)Mathf.Floor(soulPower * 0.08f * (1 + 0.01f * rbp.iAtkDegree))) + "\n" + + string.Format(GetStringFromTable(8132), (int)Mathf.Floor(soulPower * 0.0002f + 1)) + "\n" + + string.Format(GetStringFromTable(8133), soulPower * 0.0006f) + "\n" + + string.Format(GetStringFromTable(8134), (int)Mathf.Floor(soulPower * 0.5f)); + } + SetText(m_pNew_SoulPower, soulHint); + + SetText(m_pTxt_Stealth, rbp.iInvisibleDegree.ToString()); + SetText(m_pTxt_Antistealth, rbp.iAntiInvisibleDegree.ToString()); + SetText(m_pTxt_Penetration, rbp.iPenetration.ToString()); + SetText(m_pNew_Penetration, string.Format(GetStringFromTable(9380), (int)(100 * rbp.iPenetration * 3 / (float)(rbp.iPenetration + 300)))); + SetText(m_pTxt_Resilience, rbp.iResilience.ToString()); + SetText(m_pNew_Resilience, string.Format(GetStringFromTable(9381), nLevel, (int)(100 * rbp.iResilience / (float)(rbp.iResilience + nLevel)))); + + int vigourRealm = 0; + int realmLevel = host.GetRealmLevel(); + if (m_pTxt_Realm != null) m_pTxt_Realm.gameObject.SetActive(realmLevel > 0); + if (m_pPro_RealmExp != null) m_pPro_RealmExp.gameObject.SetActive(realmLevel > 0); + if (realmLevel > 0) + { + SetText(m_pTxt_Realm, GetRealmDisplayName(realmLevel)); + if (m_pPro_RealmExp != null) + { + int requireExp = 0; // TODO: host.GetRealmRequireExp() when exposed + if (requireExp > 0) + { + int realmExp = 0; // TODO: host.GetRealmExp() when exposed + m_pPro_RealmExp.value = Mathf.Clamp01((float)Math.Max(realmExp, 0) / requireExp); + SetText(m_pNew_Vigour, string.Format(GetGameUIMan()?.GetStringFromTable(11164) ?? "", realmExp, requireExp)); + } + else + { + m_pPro_RealmExp.value = 1f; + } + } + } + SetText(m_pTxt_Vigour, rbp.iVigour.ToString()); + SetText(m_pNew_Vigour, string.Format(GetStringFromTable(11146), vigourRealm)); + + int forceId = host.GetForce(); + if (m_pBtn_Force != null) m_pBtn_Force.interactable = forceId > 0; + if (m_pTxt_Force != null) + SetText(m_pTxt_Force, ""); // TODO: force name when GetForceMgr/GetForceData exposed + if (m_pTxt_Title != null) + SetText(m_pTxt_Title, ""); // TODO: title when GetCurrentTitle/GetTitleConfig exposed + if (m_pBtn_Book != null) m_pBtn_Book.interactable = host.GetReincarnationCount() > 0; + } + + private static void SetText(TextMeshProUGUI t, string s) + { + if (t != null) t.text = s ?? ""; + } + + private static void SetColor(TextMeshProUGUI t, Color c) + { + if (t != null) t.color = c; + } + + private static float DefenceToPercent(int def, int nLevel) + { + float f = 100f * def / (def + 40f * nLevel - 25f); + return Mathf.Min(f, 95f); + } + + private string GetRealmDisplayName(int realmLevel) + { + if (realmLevel <= 0) return ""; + string s = GetGameUIMan()?.GetStringFromTable(11000 + realmLevel); + return s ?? ""; + } + + private int[] CalcEquipmentBonus(CECHostPlayer host) + { + int[] iFourPro = { 0, 0, 0, 0 }; + var inv = host?.GetEquipment(); + if (inv == null) return iFourPro; + int equipSlots = InventoryConst.SIZE_ALL_EQUIPIVTR; + for (int i = 0; i < equipSlots; i++) + { + var item = inv.GetItem(i); + if (item is EC_IvtrEquip equ && equ.Props != null) + { + int n = equ.Props.Count; + for (int j = 0; j < n; j++) + { + var pro = equ.Props[j]; + int byPropType = pro.Type; // use Type as prop type; map via GetItemExtPropType if needed + int val = (pro.Params != null && pro.Params.Length > 0) ? pro.Params[0] : 0; + if (byPropType >= 41 && byPropType <= 44) + iFourPro[byPropType - 41] += val; + else if (byPropType >= 106 && byPropType <= 108) + iFourPro[byPropType - 106] += val; + else if (byPropType >= 95 && byPropType <= 98) + iFourPro[byPropType - 95] += val; + else if (byPropType >= 128 && byPropType <= 131) + { + int[] order = { 3, 0, 1, 2 }; + iFourPro[order[byPropType - 128]] += val; + } + } + } + } + return iFourPro; + } + } + + /// Forwards drag on the portrait RawImage to DlgCharacter for rotation. + public class DlgCharacterPortraitDragReceiver : MonoBehaviour, IBeginDragHandler, IDragHandler + { + private DlgCharacter m_dlg; + + public void Init(DlgCharacter dlg) { m_dlg = dlg; } + + public void OnBeginDrag(PointerEventData eventData) { } + + public void OnDrag(PointerEventData eventData) + { + if (m_dlg != null) + m_dlg.OnPortraitDragDelta(eventData.delta.x); + } + } +} diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs.meta b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs.meta new file mode 100644 index 0000000000..912422237c --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgCharacter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 72e0e1668d68ee24284bf5db688c27a0 \ No newline at end of file