using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using ModelRenderer.Scripts.GameData; using BrewMonster; using BrewMonster.Common; using BrewMonster.Network; using System.IO; using System.Text.RegularExpressions; using System.Reflection; using BrewMonster.Scripts.Managers; namespace PerfectWorld.Scripts.Managers { /// /// Equipment item class that handles all equipment-specific functionality /// Converted from C++ EC_IvtrEquip.cpp /// public class EC_IvtrEquip { #region Constants and Enums // Item Class IDs public const int ICID_EQUIP = 1; public const int ICID_WEAPON = 2; // Item Made From Types public const byte IMT_NULL = 0; public const byte IMT_SIGN = 1; // Property Effect Essence Flags public const uint PEE_PHYDAMAGE = 0x00000001; public const uint PEE_MAGICDAMAGE = 0x00000002; public const uint PEE_PHYDEF = 0x00000004; public const uint PEE_GOLDDEF = 0x00000008; public const uint PEE_WOODDEF = 0x00000010; public const uint PEE_WATERDEF = 0x00000020; public const uint PEE_FIREDEF = 0x00000040; public const uint PEE_EARTHDEF = 0x00000080; public const uint PEE_ATKSPEED = 0x00000100; public const uint PEE_ATKDIST = 0x00000200; public const uint PEE_HP = 0x00000400; public const uint PEE_MP = 0x00000800; public const uint PEE_DODGE = 0x00001000; public const uint PEE_ENDURANCE = 0x00002000; public const uint PEE_STRENGTHREQ = 0x00004000; public const uint PEE_AGILITYREQ = 0x00008000; public const uint PEE_ENERGYREQ = 0x00010000; public const uint PEE_VITALITYREQ = 0x00020000; // Property Effect Essence Indexes public const int PEEI_PHYDAMAGE = 0; public const int PEEI_MAX_PHYDAMAGE = 1; public const int PEEI_MAGICDAMAGE = 2; public const int PEEI_MAX_MAGICDAMAGE = 3; public const int PEEI_PHYDEF = 4; public const int PEEI_GOLDDEF = 5; public const int PEEI_WOODDEF = 6; public const int PEEI_WATERDEF = 7; public const int PEEI_FIREDEF = 8; public const int PEEI_EARTHDEF = 9; public const int PEEI_ATKDIST = 10; public const int PEEI_HP = 11; public const int PEEI_MP = 12; public const int PEEI_DODGE = 13; public const int MAX_PEEINDEX = 14; // Refine Indexes public const int REFINE_PHYDAMAGE = 0; public const int REFINE_MAGICDAMAGE = 1; public const int REFINE_PHYDEF = 2; public const int REFINE_GOLDDEF = 3; public const int REFINE_WOODDEF = 4; public const int REFINE_WATERDEF = 5; public const int REFINE_FIREDEF = 6; public const int REFINE_EARTHDEF = 7; public const int REFINE_HP = 8; public const int REFINE_DODGE = 9; public const int MAX_REFINEINDEX = 10; // Item Description Colors public const int ITEMDESC_COL_WHITE = 0; public const int ITEMDESC_COL_GREEN = 1; public const int ITEMDESC_COL_YELLOW = 2; public const int ITEMDESC_COL_DARKGOLD = 3; public const int ITEMDESC_COL_LIGHTBLUE = 4; public const int ITEMDESC_COL_CYANINE = 5; public const int ITEMDESC_COL_RED = 6; public const int ITEMDESC_COL_GRAY = 7; // Item Description String IDs public const int ITEMDESC_NAME = 1000; public const int ITEMDESC_ADDPHYDAMAGE = 1001; public const int ITEMDESC_MAXPHYDAMAGE = 1002; public const int ITEMDESC_ADDMAGICDAMAGE = 1003; public const int ITEMDESC_MAXMAGICDAMAGE = 1004; public const int ITEMDESC_PHYDEFENCE = 1005; public const int ITEMDESC_ALLMAGICDEF = 1006; public const int ITEMDESC_GOLDDEFENCE = 1007; public const int ITEMDESC_WOODDEFENCE = 1008; public const int ITEMDESC_WATERDEFENCE = 1009; public const int ITEMDESC_FIREDEFENCE = 1010; public const int ITEMDESC_EARTHDEFENCE = 1011; public const int ITEMDESC_ADDHP = 1012; public const int ITEMDESC_ADDMP = 1013; public const int ITEMDESC_STRENGTH = 1014; public const int ITEMDESC_AGILITY = 1015; public const int ITEMDESC_ENERGY = 1016; public const int ITEMDESC_VITALITY = 1017; public const int ITEMDESC_DODGE = 1018; public const int ITEMDESC_DEADLYSTRIKE = 1019; public const int ITEMDESC_ATKRATING = 1020; public const int ITEMDESC_ATKTIME = 1021; public const int ITEMDESC_ADDATKDIST = 1022; public const int ITEMDESC_CASTTIME = 1023; public const int ITEMDESC_ENDURANCE = 1024; public const int ITEMDESC_REPAIRCOST = 1025; public const int ITEMDESC_EQUIPMARK = 1026; public const int ITEMDESC_MADEFROM = 1027; public const int ITEMDESC_2STRINGS = 1028; public const int ITEMDESC_REPUTATION_REQ = 1029; public const int ITEMDESC_EQUIP_DESTROYING = 1030; public const int ITEMDESC_EQUIP_REPAIR_NEED_ITEM = 1031; public const int ITEMDESC_EQUIP_REPAIR_NEED_ITEMCNT = 1032; public const int ITEMDESC_ERRORPROP = 1033; public const int ITEMDESC_RANDOMPROP = 1034; public const int ITEMDESC_ATK_DEGREE = 1035; public const int ITEMDESC_DEF_DEGREE = 1036; public const int ITEMDESC_SOULPOWER = 1037; public const int ITEMDESC_VIGOUR = 1038; public const int ITEMDESC_ADDRIDEONPETSPEED = 1039; public const int ITEMDESC_PENETRATION = 1040; public const int ITEMDESC_RESILIENCE = 1041; public const int ITEMDESC_PROFVIEW = 1042; public const int ITEMDESC_REQEXTRA = 1043; public const int ITEMDESC_HPRECOVER = 1044; public const int ITEMDESC_MPRECOVER = 1045; public const int ITEMDESC_RUNSPEED = 1046; public const int ITEMDESC_RUNSPEEDEXTRA = 1047; public const int ITEMDESC_PHYRESIST = 1048; public const int ITEMDESC_TOTAL_DEFENCE_ADD = 1049; public const int ITEMDESC_GOLDRESIST = 1050; public const int ITEMDESC_WOODRESIST = 1051; public const int ITEMDESC_WATERRESIST = 1052; public const int ITEMDESC_FIRERESIST = 1053; public const int ITEMDESC_EARTHRESIST = 1054; public const int ITEMDESC_ALLMAGICRESIST = 1055; public const int ITEMDESC_EXP = 1056; // Scale Types public const int SCALE_SELL = 1; // Endurance Scale public const int ENDURANCE_SCALE = 100; #endregion #region Fields // Basic Item Properties public int TemplateId { get; set; } public int ExpireDate { get; set; } public int CID { get; set; } public int Price { get; set; } public int Count { get; set; } public float PriceScale { get; set; } public int ScaleType { get; set; } // Equipment Requirements public int LevelReq { get; set; } public int StrengthReq { get; set; } public int AgilityReq { get; set; } public int ProfReq { get; set; } public int VitalityReq { get; set; } public int EnergyReq { get; set; } public int ReputationReq { get; set; } // Endurance public int CurEndurance { get; set; } public int MaxEndurance { get; set; } public int RepairFee { get; set; } // Maker Information public byte MadeFrom { get; set; } public string Maker { get; set; } // Equipment Properties public ushort StoneMask { get; set; } public int FixProps { get; set; } public int RefineLvl { get; set; } public byte PropNum { get; set; } public byte EmbedNum { get; set; } // Equipment Arrays public List Holes { get; set; } public List Props { get; set; } // Description protected string m_strDesc = ""; #endregion #region Base Stats (from Element Data) private static bool TryGetNumber(object data, string[] names, out T value) where T : struct { value = default; if (data == null) return false; var t = data.GetType(); foreach (var name in names) { var f = t.GetField(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase); if (f != null) { try { object v = f.GetValue(data); if (v == null) continue; if (typeof(T) == typeof(float)) { float fv = Convert.ToSingle(v); value = (T)(object)fv; return true; } else { int iv = Convert.ToInt32(v); if (typeof(T) == typeof(int)) { value = (T)(object)iv; return true; } } } catch { } } var p = t.GetProperty(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase); if (p != null) { try { object v = p.GetValue(data, null); if (v == null) continue; if (typeof(T) == typeof(float)) { float fv = Convert.ToSingle(v); value = (T)(object)fv; return true; } else { int iv = Convert.ToInt32(v); if (typeof(T) == typeof(int)) { value = (T)(object)iv; return true; } } } catch { } } } return false; } public string GetBaseStatsForDisplay() { return GetBaseStatsDesc(); } private string GetBaseStatsDesc() { elementdataman edm = ElementDataManProvider.GetElementDataMan(); if (edm == null) return string.Empty; try { DATA_TYPE dt = DATA_TYPE.DT_INVALID; object data = edm.get_data_ptr(unchecked((uint)TemplateId), ID_SPACE.ID_SPACE_ESSENCE, ref dt); if (data == null) data = TryFindElementByScanningArraysLocal(edm, unchecked((uint)TemplateId)); if (data == null) return string.Empty; // Try weapon-style fields first int dmgLow = 0, dmgHighMax = 0; bool hasDmgLow = TryGetNumber(data, new[] { "damage_low" }, out dmgLow); bool hasDmgHighMax = TryGetNumber(data, new[] { "damage_high_max", "damage_high" }, out dmgHighMax); List lines = new List(); if (hasDmgLow && hasDmgHighMax) { // Physical damage range lines.Add(string.Format("{0} {1}-{2}", GetItemDescString(ITEMDESC_ADDPHYDAMAGE), dmgLow, dmgHighMax)); // Magic damage if available if (TryGetNumber(data, new[] { "magic_damage_low" }, out int mdLow) && TryGetNumber(data, new[] { "magic_damage_high_max", "magic_damage_high" }, out int mdHigh)) { lines.Add(string.Format("{0} {1}-{2}", GetItemDescString(ITEMDESC_ADDMAGICDAMAGE), mdLow, mdHigh)); } // Attack time/speed float atkTime; if (TryGetNumber(data, new[] { "attack_time", "atk_time" }, out atkTime) && atkTime > 0.0001f) { // Show speed in attacks per second (1 / time) float aps = 1.0f / atkTime; lines.Add(string.Format("{0} {1:F2}", GetItemDescString(ITEMDESC_ATKTIME), aps)); } else if (TryGetNumber(data, new[] { "attack_speed", "atk_speed" }, out float atkSpeed) && atkSpeed > 0) { lines.Add(string.Format("{0} {1:F2}", GetItemDescString(ITEMDESC_ATKTIME), atkSpeed)); } // Range float range; if (TryGetNumber(data, new[] { "range", "attack_range", "atk_range" }, out range)) { lines.Add(string.Format("{0} {1:F2}", GetItemDescString(ITEMDESC_ADDATKDIST), range)); } } else { // Try armor-style fields if (TryGetNumber(data, new[] { "defence_high", "defense_high", "defence", "defense" }, out int defHigh)) { lines.Add(string.Format("{0} +{1}", GetItemDescString(ITEMDESC_PHYDEFENCE), defHigh)); } // Elemental resistances (look for *_resist or specific names) (string label, string[] names)[] res = new (string, string[])[] { (GetItemDescString(ITEMDESC_GOLDDEFENCE), new[]{"resist_metal","resistance_metal","gold_resist"}), (GetItemDescString(ITEMDESC_WOODDEFENCE), new[]{"resist_wood","resistance_wood","wood_resist"}), (GetItemDescString(ITEMDESC_WATERDEFENCE), new[]{"resist_water","resistance_water","water_resist"}), (GetItemDescString(ITEMDESC_FIREDEFENCE), new[]{"resist_fire","resistance_fire","fire_resist"}), (GetItemDescString(ITEMDESC_EARTHDEFENCE), new[]{"resist_earth","resistance_earth","earth_resist"}) }; for (int i = 0; i < res.Length; i++) { var label = res[i].label; var names = res[i].names; if (TryGetNumber(data, names, out int v)) { lines.Add(string.Format("{0} +{1}", label, v)); } } } return string.Join("\\r", lines); } catch { return string.Empty; } } private static object TryFindElementByScanningArraysLocal(object edm, uint id) { if (edm == null) return null; try { var fields = edm.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (var f in fields) { if (!f.FieldType.IsArray) continue; var arr = f.GetValue(edm) as Array; if (arr == null || arr.Length == 0) continue; var elemType = f.FieldType.GetElementType(); var idField = elemType.GetField("id", BindingFlags.Public | BindingFlags.Instance); if (idField == null || idField.FieldType != typeof(uint)) continue; for (int i = 0; i < arr.Length; i++) { var element = arr.GetValue(i); if (element == null) continue; var value = (uint)idField.GetValue(element); if (value == id) { return element; } } } } catch { } return null; } #endregion #region Property Structure public class Property { public int Type { get; set; } public int NumParam { get; set; } public bool Embed { get; set; } public bool Suite { get; set; } public bool Engraved { get; set; } public bool Local { get; set; } public int[] Params { get; set; } public Property() { Params = new int[4]; } public Property(Property other) { Type = other.Type; NumParam = other.NumParam; Embed = other.Embed; Suite = other.Suite; Engraved = other.Engraved; Local = other.Local; Params = new int[4]; Array.Copy(other.Params, Params, Math.Min(4, other.Params.Length)); } } #endregion #region Constructor public EC_IvtrEquip(int tid, int expireDate) { TemplateId = tid; ExpireDate = expireDate; CID = ICID_EQUIP; Price = 0; Count = 1; PriceScale = 1.0f; ScaleType = 0; LevelReq = 0; StrengthReq = 0; AgilityReq = 0; ProfReq = 0; VitalityReq = 0; EnergyReq = 0; ReputationReq = 0; CurEndurance = 0; MaxEndurance = 0; RepairFee = 0; MadeFrom = 0; StoneMask = 0; FixProps = 0; RefineLvl = 0; PropNum = 0; EmbedNum = 0; Holes = new List(); Props = new List(); } public EC_IvtrEquip(EC_IvtrEquip other) { // Copy basic properties TemplateId = other.TemplateId; ExpireDate = other.ExpireDate; CID = other.CID; Price = other.Price; Count = other.Count; PriceScale = other.PriceScale; ScaleType = other.ScaleType; // Copy equipment properties LevelReq = other.LevelReq; ProfReq = other.ProfReq; StrengthReq = other.StrengthReq; AgilityReq = other.AgilityReq; VitalityReq = other.VitalityReq; EnergyReq = other.EnergyReq; ReputationReq = other.ReputationReq; CurEndurance = other.CurEndurance; MaxEndurance = other.MaxEndurance; RepairFee = other.RepairFee; MadeFrom = other.MadeFrom; Maker = other.Maker; StoneMask = other.StoneMask; FixProps = other.FixProps; RefineLvl = other.RefineLvl; PropNum = other.PropNum; EmbedNum = other.EmbedNum; Holes = new List(other.Holes); Props = new List(other.Props.Select(p => new Property(p))); } #endregion #region Core Methods /// /// Set item detail information from binary data /// public bool SetItemInfo(byte[] infoData, int dataLen) { if (infoData == null || dataLen == 0) return true; // Try native order (as original client): // [6 x short requirements][2 x int endurance][short essenceSize][maker info][essence bytes][short numHole][WORD stoneMask][numHole x int holes][int numProp][props] if (TryParseEquipInfoNative(infoData, dataLen)) { ParseProperties(); return true; } // Fallback to legacy/custom order if server payload differs if (TryParseEquipInfoLegacy(infoData, dataLen)) { ParseProperties(); return true; } Debug.LogError("EC_IvtrEquip::SetItemInfo: could not parse detail payload"); return false; } private bool TryParseEquipInfoNative(byte[] data, int len) { try { int offset = 0; if (len < 6 * 2 + 2 * 4 + 2) return false; LevelReq = BitConverter.ToInt16(data, offset); offset += 2; ProfReq = BitConverter.ToInt16(data, offset); offset += 2; StrengthReq= BitConverter.ToInt16(data, offset); offset += 2; VitalityReq= BitConverter.ToInt16(data, offset); offset += 2; AgilityReq = BitConverter.ToInt16(data, offset); offset += 2; EnergyReq = BitConverter.ToInt16(data, offset); offset += 2; CurEndurance = BitConverter.ToInt32(data, offset); offset += 4; MaxEndurance = BitConverter.ToInt32(data, offset); offset += 4; int essenceSize = BitConverter.ToInt16(data, offset); offset += 2; // Maker info (type + length + payload) if (offset + 2 <= len) { ReadMakerInfo(data, ref offset); } if (essenceSize < 0 || offset + essenceSize > len) return false; offset += essenceSize; // skip essence for now if (offset + 2 + 2 > len) return false; int numHole = BitConverter.ToInt16(data, offset); offset += 2; StoneMask = BitConverter.ToUInt16(data, offset); offset += 2; Holes.Clear(); if (numHole > 0) { if (offset + 4 * numHole > len) return false; Holes.Capacity = numHole; for (int i = 0; i < numHole; i++) { Holes.Add(BitConverter.ToInt32(data, offset)); offset += 4; } } else if (numHole < 0) { return false; } if (offset + 4 > len) return false; int numProp = BitConverter.ToInt32(data, offset); offset += 4; Props.Clear(); if (numProp > 0) { Props.Capacity = numProp; for (int i = 0; i < numProp; i++) { if (offset + 4 > len) return false; int type = BitConverter.ToInt32(data, offset); offset += 4; Property prop = new Property(); prop.Type = type & 0x1fff; prop.NumParam = (type & 0x6000) >> 13; prop.Embed = (type & 0x8000) != 0; prop.Suite = (type & 0x10000) != 0; prop.Engraved = (type & 0x20000) != 0; prop.Local = false; for (int j = 0; j < prop.NumParam; j++) { if (offset + 4 > len) return false; prop.Params[j] = BitConverter.ToInt32(data, offset); offset += 4; } Props.Add(prop); } } else if (numProp < 0) { return false; } // Sanity check to catch misalignment if (LevelReq < 0 || LevelReq > 2000) return false; if (MaxEndurance < 0 || MaxEndurance > 1000000) return false; return true; } catch { return false; } } private bool TryParseEquipInfoLegacy(byte[] data, int len) { try { int offset = 0; if (len < 16 + 6 * 2 + 8 + 2) return false; // A legacy format we used earlier that prefixed price/scale before requirements Price = BitConverter.ToInt32(data, offset); offset += 4; Count = BitConverter.ToInt32(data, offset); offset += 4; PriceScale = BitConverter.ToSingle(data, offset); offset += 4; ScaleType = BitConverter.ToInt32(data, offset); offset += 4; LevelReq = BitConverter.ToInt16(data, offset); offset += 2; ProfReq = BitConverter.ToInt16(data, offset); offset += 2; StrengthReq= BitConverter.ToInt16(data, offset); offset += 2; VitalityReq= BitConverter.ToInt16(data, offset); offset += 2; AgilityReq = BitConverter.ToInt16(data, offset); offset += 2; EnergyReq = BitConverter.ToInt16(data, offset); offset += 2; CurEndurance = BitConverter.ToInt32(data, offset); offset += 4; MaxEndurance = BitConverter.ToInt32(data, offset); offset += 4; int essenceSize = BitConverter.ToInt16(data, offset); offset += 2; ReadMakerInfo(data, ref offset); if (essenceSize < 0 || offset + essenceSize > len) return false; offset += essenceSize; int numHole = BitConverter.ToInt16(data, offset); offset += 2; StoneMask = BitConverter.ToUInt16(data, offset); offset += 2; Holes.Clear(); if (numHole > 0) { if (offset + 4 * numHole > len) return false; for (int i = 0; i < numHole; i++) { Holes.Add(BitConverter.ToInt32(data, offset)); offset += 4; } } else if (numHole < 0) return false; if (offset + 4 > len) return false; int numProp = BitConverter.ToInt32(data, offset); offset += 4; Props.Clear(); if (numProp > 0) { for (int i = 0; i < numProp; i++) { if (offset + 4 > len) return false; int type = BitConverter.ToInt32(data, offset); offset += 4; Property prop = new Property(); prop.Type = type & 0x1fff; prop.NumParam = (type & 0x6000) >> 13; prop.Embed = (type & 0x8000) != 0; prop.Suite = (type & 0x10000) != 0; prop.Engraved = (type & 0x20000) != 0; for (int j = 0; j < prop.NumParam; j++) { if (offset + 4 > len) return false; prop.Params[j] = BitConverter.ToInt32(data, offset); offset += 4; } Props.Add(prop); } } return true; } catch { return false; } } /// /// Read maker information from binary data /// private void ReadMakerInfo(byte[] data, ref int offset) { MadeFrom = data[offset++]; int makerLen = data[offset++]; if (makerLen > 0) { if (MadeFrom == IMT_SIGN) { ushort color = BitConverter.ToUInt16(data, offset); offset += 2; makerLen -= 2; string maker = System.Text.Encoding.Unicode.GetString(data, offset, makerLen); offset += makerLen; if (string.IsNullOrEmpty(maker)) { Debug.LogWarning($"EC_IvtrEquip::ReadMakerInfo: Invalid maker info with makerLen={makerLen + 2}"); return; } SetNewMark(maker, ColorFromWord(color)); } else { Maker = System.Text.Encoding.Unicode.GetString(data, offset, makerLen); offset += makerLen; } } else { Maker = ""; } } /// /// Set new mark with color /// public void SetNewMark(string mark, Color color) { Maker = mark; if (!string.IsNullOrEmpty(Maker)) { // Convert hint string // Filter bad words // g_pGame->GetGameRun()->GetUIManager()->FilterBadWords(m_strMaker); // Add color using ^RRGGBB from Color32 bytes Color32 c32 = color; string colorStr = $"^{c32.r:X2}{c32.g:X2}{c32.b:X2}"; Maker = colorStr + Maker; // Add equipment mark display string Maker = string.Format(GetItemDescString(ITEMDESC_EQUIPMARK), Maker); } MadeFrom = string.IsNullOrEmpty(mark) ? IMT_NULL : IMT_SIGN; } #endregion #region Utility Methods /// /// Get item name /// public string GetName() { return EC_IvtrItem.ResolveItemName(TemplateId); } /// /// Add current endurance /// public int AddCurEndurance(int value) { CurEndurance += value; CurEndurance = Mathf.Clamp(CurEndurance, 0, MaxEndurance); return CurEndurance; } /// /// Convert endurance real value to displaying value /// public static int VisualizeEndurance(int v) { return (v + ENDURANCE_SCALE - 1) / ENDURANCE_SCALE; } /// /// Get empty hole number /// public int GetEmptyHoleNum() { int count = 0; foreach (int hole in Holes) { if (hole == 0) count++; } return count; } /// /// Get repair cost /// public int GetRepairCost() { if (MaxEndurance == 0 || MaxEndurance == CurEndurance) return 0; int cost = (int)GetRawRepairCost(); if (cost < 1) cost = 1; return cost; } /// /// Get raw repair cost /// public float GetRawRepairCost() { float cost = 0.0f; if (!IsRepairable()) return cost; if (CurEndurance == 0) { cost = RepairFee; } else if (MaxEndurance > 0 && CurEndurance < MaxEndurance) { float factor = (MaxEndurance - CurEndurance) / (float)MaxEndurance; cost = RepairFee * factor; } return cost; } /// /// Get scaled item price /// public int GetScaledPrice() { if (ScaleType != SCALE_SELL) return (int)(Price * Count * PriceScale + 0.5f); int price = Price * Count; if (MaxEndurance == CurEndurance || MaxEndurance == 0) return (int)(price * PriceScale + 0.5f); else return (int)(price * PriceScale * CurEndurance / (float)MaxEndurance + 0.5f); } /// /// Check if item is repairable /// public bool IsRepairable() { return MaxEndurance > 0; } /// /// Check if item is destroying /// public bool IsDestroying() { return CurEndurance == 0 && MaxEndurance > 0; } /// /// Check if item is rare /// public bool IsRare() { return RefineLvl >= 3; } #endregion #region Property System /// /// Get property effect essence flags /// public uint PropEffectEssence() { uint flags = 0; foreach (Property prop in Props) { flags = PropEffectMask(prop, flags); } return flags; } /// /// Get property effect mask for a specific property /// public uint PropEffectMask(Property prop, uint flags) { // Get property type from game's item ext prop table byte propType = GetPropertyType(prop.Type); switch (propType) { case 0: // Physical damage flags |= PEE_PHYDAMAGE; break; case 1: // Max physical damage flags |= PEE_PHYDAMAGE; break; case 2: // Physical damage (%) flags |= PEE_PHYDAMAGE; break; case 3: // Magic damage flags |= PEE_MAGICDAMAGE; break; case 4: // Max magic damage case 5: // Magic damage (%) flags |= PEE_MAGICDAMAGE; break; case 6: // +Def -Atk flags |= PEE_PHYDAMAGE; flags |= PEE_PHYDEF; break; case 7: // +Atk -Def flags |= PEE_PHYDAMAGE; flags |= PEE_PHYDEF; break; case 8: // +Magic -MagicDef flags |= PEE_MAGICDAMAGE; flags |= PEE_GOLDDEF; flags |= PEE_WOODDEF; flags |= PEE_WATERDEF; flags |= PEE_FIREDEF; flags |= PEE_EARTHDEF; break; case 9: // Attack speed flags |= PEE_ATKSPEED; break; case 10: // Attack distance flags |= PEE_ATKDIST; break; case 11: // Cast time break; case 12: // Physical defense flags |= PEE_PHYDEF; break; case 13: // Physical defense (%) flags |= PEE_PHYDEF; break; case 14: // All magic defense flags |= PEE_GOLDDEF; flags |= PEE_WOODDEF; flags |= PEE_WATERDEF; flags |= PEE_FIREDEF; flags |= PEE_EARTHDEF; break; case 15: // Gold defense flags |= PEE_GOLDDEF; break; case 16: // Gold defense (%) flags |= PEE_GOLDDEF; break; case 17: // Wood defense flags |= PEE_WOODDEF; break; case 18: // Wood defense (%) flags |= PEE_WOODDEF; break; case 19: // Water defense flags |= PEE_WATERDEF; break; case 20: // Water defense (%) flags |= PEE_WATERDEF; break; case 21: // Fire defense flags |= PEE_FIREDEF; break; case 22: // Fire defense (%) flags |= PEE_FIREDEF; break; case 23: // Earth defense flags |= PEE_EARTHDEF; break; case 24: // Earth defense (%) flags |= PEE_EARTHDEF; break; case 30: // +Gold -Fire flags |= PEE_GOLDDEF; flags |= PEE_FIREDEF; break; case 31: // +Wood -Gold flags |= PEE_GOLDDEF; flags |= PEE_WOODDEF; break; case 32: // +Water -Earth flags |= PEE_WATERDEF; flags |= PEE_EARTHDEF; break; case 33: // +Fire -Water flags |= PEE_WATERDEF; flags |= PEE_FIREDEF; break; case 34: // +Earth -Wood flags |= PEE_WOODDEF; flags |= PEE_EARTHDEF; break; case 35: // HP flags |= PEE_HP; break; case 36: // MP flags |= PEE_MP; break; case 37: // HP (%) flags |= PEE_HP; break; case 38: // MP (%) flags |= PEE_MP; break; case 50: // Dodge flags |= PEE_DODGE; break; case 53: // Endurance (%) flags |= PEE_ENDURANCE; break; case 56: // Equipment requirements flags |= PEE_STRENGTHREQ; flags |= PEE_AGILITYREQ; flags |= PEE_ENERGYREQ; flags |= PEE_VITALITYREQ; break; case 92: // +Physical +Magic flags |= PEE_PHYDEF; flags |= PEE_GOLDDEF; flags |= PEE_WOODDEF; flags |= PEE_WATERDEF; flags |= PEE_FIREDEF; flags |= PEE_EARTHDEF; break; case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: // refine props – do not change mask further break; } return flags; } /// /// Get property type from property ID /// private static Dictionary s_propIdToType; private static bool s_propMapLoaded; private static readonly object s_propLock = new object(); private static void EnsurePropMapLoaded() { if (s_propMapLoaded) return; lock (s_propLock) { if (s_propMapLoaded) return; s_propIdToType = new Dictionary(); try { // Parse configs/item_ext_prop.txt to learn valid property type ids string path = Path.Combine(UnityEngine.Application.streamingAssetsPath, "configs/item_ext_prop.txt"); if (File.Exists(path)) { int currentType = -1; bool inTypeBlock = false; bool inBlockComment = false; foreach (var rawLine in File.ReadLines(path)) { string src = rawLine; if (string.IsNullOrEmpty(src)) continue; // strip block comments /* ... */ possibly spanning lines System.Text.StringBuilder sb = new System.Text.StringBuilder(); int i = 0; while (i < src.Length) { if (inBlockComment) { int end = src.IndexOf("*/", i, StringComparison.Ordinal); if (end < 0) { i = src.Length; break; } inBlockComment = false; i = end + 2; continue; } int start = src.IndexOf("/*", i, StringComparison.Ordinal); if (start < 0) { sb.Append(src, i, src.Length - i); break; } sb.Append(src, i, start - i); int end2 = src.IndexOf("*/", start + 2, StringComparison.Ordinal); if (end2 < 0) { inBlockComment = true; break; } i = end2 + 2; } string line = sb.ToString().Trim(); if (line.Length == 0) continue; if (line.StartsWith("//")) continue; // Detect a new type section: e.g., "type: 45" var typeMatch = Regex.Match(line, "^type:\\s*(?\\d+)"); if (typeMatch.Success) { if (int.TryParse(typeMatch.Groups["type"].Value, out int t)) currentType = t; inTypeBlock = line.Contains("{"); if (line.Contains("}")) { inTypeBlock = false; currentType = -1; } continue; } if (line.Contains("{")) inTypeBlock = true; if (line.Contains("}")) { inTypeBlock = false; currentType = -1; } if (inTypeBlock && currentType >= 0) { foreach (Match m in Regex.Matches(line, "\\b(?\\d{1,6})\\b")) { if (int.TryParse(m.Groups["id"].Value, out int id)) { if (!s_propIdToType.ContainsKey(id)) s_propIdToType[id] = (byte)Mathf.Clamp(currentType, 0, 255); } } } } } } catch { /* ignore parse errors; fallback below will handle */ } s_propMapLoaded = true; } } private byte GetPropertyType(int propId) { // Load once from item_ext_prop.txt; if not enough info, fall back to heuristics EnsurePropMapLoaded(); if (s_propIdToType != null && s_propIdToType.TryGetValue(propId, out byte mapped)) return mapped; // Unknown return 0xff; } /// /// Set properties to local /// public void SetLocalProps() { if (Props.Count == 0) return; foreach (Property prop in Props) { prop.Local = true; } } /// /// Get deadly strike rate provided by this equipment /// public int GetDeadlyStrikeRate(bool suiteGen) { int val = 0; if (suiteGen) { // Suite generation logic would go here // This is a simplified version } else { foreach (Property prop in Props) { byte propType = GetPropertyType(prop.Type); if (propType == 45 || propType == 110) val += prop.Params[0]; } } return val; } /// /// Decide equipment name color /// public int DecideNameCol() { int index = GetColorStrID(TemplateId); if (index >= 0) return index; int col = ITEMDESC_COL_WHITE; switch (FixProps) { case 1: col = ITEMDESC_COL_GREEN; break; case 2: col = ITEMDESC_COL_YELLOW; break; case 3: col = ITEMDESC_COL_DARKGOLD; break; default: if (PropNum > 0) col = ITEMDESC_COL_LIGHTBLUE; break; } return col; } /// /// Get color string ID for template /// private int GetColorStrID(int templateId) { // This would normally query the game's color system return -1; } #endregion #region Description Methods /// /// Get item description for booth buying /// public string GetBoothBuyDesc() { m_strDesc = ""; int white = ITEMDESC_COL_WHITE; // Item name AddDescText(white, true, GetItemDescString(ITEMDESC_NAME), GetName()); // Base stats from element data string baseStats = GetBaseStatsDesc(); if (!string.IsNullOrEmpty(baseStats)) { m_strDesc += GetColorString(ITEMDESC_COL_WHITE); m_strDesc += baseStats; m_strDesc += "\\r"; } // Add-on properties BuildAddOnPropDesc(null, null); // Tessera / stones BuildTesseraDesc(); // Suite info (if any) AddSuiteDesc(); // Price AddPriceDesc(white, false); return m_strDesc; } /// /// Add price description /// public void AddPriceDesc(int col, bool repair) { if (repair) AddDescText(col, false, GetItemDescString(ITEMDESC_REPAIRCOST), GetRepairCost()); else AddDescText(col, true, "Price: {0}", GetScaledPrice()); } /// /// Format property description /// public string FormatPropDesc(Property prop) { string tmpDesc = m_strDesc; m_strDesc = ""; AddOneAddOnPropDesc(prop.Type, prop.Params, null, null, prop.Local); string result = m_strDesc; m_strDesc = tmpDesc; return result; } /// /// Format refine data into a string /// public string FormatRefineData(uint addonId) { // This would normally query the equipment addon data // For now, return empty string return ""; } /// /// Check property range value /// public bool CheckPropRangeValue(Property prop) { int idProp = prop.Type; byte propType = GetPropertyType(idProp); switch (propType) { case 25: // +Gold(%) -Fire(%) case 26: // +Wood(%) -Gold(%) case 27: // +Water(%) -Earth(%) case 28: // +Fire(%) -Water(%) case 29: // +Earth(%) -Wood(%) case 30: // +Gold -Fire case 31: // +Wood -Gold case 32: // +Water -Earth case 33: // +Fire -Water case 34: // +Earth -Wood case 55: // Add skill return false; } // Check for range values in equipment addon data // This would normally query the equipment addon data return false; } #endregion #region Helper Methods /// /// Parse properties /// private void ParseProperties() { RefineLvl = 0; PropNum = 0; EmbedNum = 0; if (Props.Count == 0) return; foreach (Property prop in Props) { int level = 0; if (prop.Embed) { EmbedNum++; continue; } else if (prop.Suite) continue; else if (prop.Engraved) continue; byte propType = GetPropertyType(prop.Type); switch (propType) { case 200: // Refine physical damage case 201: // Refine magic damage case 202: // Refine physical defense case 203: // Refine gold defense case 204: // Refine wood defense case 205: // Refine water defense case 206: // Refine fire defense case 207: // Refine earth defense case 208: // Refine HP case 209: // Refine dodge case 210: // Refine all magic defense case 211: // Refine physical damage & magic damage case 212: // Refine physical defense & magic defense level = prop.Params[1]; break; } if (level > 0) RefineLvl = level; else PropNum++; } } /// /// Check if this equipment belongs to a suite /// public int GetSuiteID() { // This would normally query the game's suite equip table return 0; } /// /// Get item description string by ID /// private string GetItemDescString(int id) { var tab = EC_Game.GetItemDesc(); if (tab != null && tab.IsInitialized()) { string s = tab.GetString(id); if (!string.IsNullOrEmpty(s)) return s; } // Fallback labels for common IDs when the table lacks #_index entries switch (id) { case ITEMDESC_ADDPHYDAMAGE: return "Công vật lý"; case ITEMDESC_ADDMAGICDAMAGE: return "TC phép thuật"; case ITEMDESC_PHYDEFENCE: return "Thủ vật lý"; case ITEMDESC_GOLDDEFENCE: return "Kháng Kim"; case ITEMDESC_WOODDEFENCE: return "Kháng Mộc"; case ITEMDESC_WATERDEFENCE: return "Kháng Thủy"; case ITEMDESC_FIREDEFENCE: return "Kháng Hỏa"; case ITEMDESC_EARTHDEFENCE: return "Kháng Thổ"; case ITEMDESC_ATKTIME: return "Tốc độ TC(lần/giây)"; case ITEMDESC_ADDATKDIST: return "Phạm vi tấn công"; case ITEMDESC_NAME: return "Tên"; case ITEMDESC_ERRORPROP: return "Thuộc tính không xác định {0}"; case ITEMDESC_ADDHP: return "HP"; case ITEMDESC_ADDMP: return "MP"; case ITEMDESC_DODGE: return "Né tránh"; case ITEMDESC_DEADLYSTRIKE: return "Chí mạng"; case ITEMDESC_HPRECOVER: return "Hồi phục HP"; case ITEMDESC_MPRECOVER: return "Hồi phục MP"; case ITEMDESC_RUNSPEED: return "Tốc độ chạy"; case ITEMDESC_RUNSPEEDEXTRA: return "Tốc độ chạy (%)"; case ITEMDESC_PHYRESIST: return "Kháng vật lý (%)"; case ITEMDESC_TOTAL_DEFENCE_ADD: return "Tổng kháng ngũ hành (%)"; case ITEMDESC_GOLDRESIST: return "Kháng Kim (%)"; case ITEMDESC_WOODRESIST: return "Kháng Mộc (%)"; case ITEMDESC_WATERRESIST: return "Kháng Thủy (%)"; case ITEMDESC_FIRERESIST: return "Kháng Hỏa (%)"; case ITEMDESC_EARTHRESIST: return "Kháng Thổ (%)"; case ITEMDESC_ALLMAGICRESIST: return "Tổng kháng phép (%)"; case ITEMDESC_EXP: return "Kinh nghiệm (%)"; default: return $"String_{id}"; } } /// /// Add description text /// private void AddDescText(int color, bool newLine, string format, params object[] args) { if (color >= 0) { string colorStr = GetColorString(color); m_strDesc += colorStr; } if (args.Length > 0) { // Accept both C#-style ("{0}") and printf-style ("%d", "%+d", "%.2f", "%%") formats string output; if (format.IndexOf('%') >= 0 && format.IndexOf('{') < 0) { output = FormatPrintfLike(format, args); } else { output = string.Format(format, args); } m_strDesc += output; } else { m_strDesc += format; } if (newLine) m_strDesc += "\\r"; } private static string FormatPrintfLike(string fmt, object[] args) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); int argIndex = 0; for (int i = 0; i < fmt.Length; i++) { char c = fmt[i]; if (c != '%') { sb.Append(c); continue; } if (i + 1 < fmt.Length && fmt[i + 1] == '%') { sb.Append('%'); i += 1; continue; } bool plus = false; int precision = -1; int j = i + 1; if (j < fmt.Length && fmt[j] == '+') { plus = true; j++; } if (j < fmt.Length && fmt[j] == '.') { // precision like %.2f j++; int start = j; while (j < fmt.Length && char.IsDigit(fmt[j])) j++; int.TryParse(fmt.Substring(start, j - start), out precision); } if (j < fmt.Length) { char spec = fmt[j]; if (spec == 'd') { object val = argIndex < args.Length ? args[argIndex++] : 0; long iv = Convert.ToInt64(val); string s = iv.ToString(); if (plus && iv >= 0) s = "+" + s; sb.Append(s); i = j; continue; } else if (spec == 'f') { object val = argIndex < args.Length ? args[argIndex++] : 0.0; double dv = Convert.ToDouble(val); string s = precision >= 0 ? dv.ToString("F" + precision) : dv.ToString("F"); if (plus && dv >= 0) s = "+" + s; sb.Append(s); i = j; continue; } } // Fallback: treat '%' as literal if format not recognized sb.Append('%'); } return sb.ToString(); } /// /// Get color string for color ID /// private string GetColorString(int colorId) { switch (colorId) { case ITEMDESC_COL_WHITE: return "^FF"; case ITEMDESC_COL_GREEN: return "^GF"; case ITEMDESC_COL_YELLOW: return "^YF"; case ITEMDESC_COL_DARKGOLD: return "^DF"; case ITEMDESC_COL_LIGHTBLUE: return "^BF"; case ITEMDESC_COL_CYANINE: return "^CF"; case ITEMDESC_COL_RED: return "^RF"; case ITEMDESC_COL_GRAY: return "^gF"; default: return "^FF"; } } /// /// Convert word color to Unity Color /// private Color ColorFromWord(ushort wordColor) { // Convert word color to Unity Color // This is a simplified conversion return new Color( ((wordColor >> 10) & 0x1F) / 31.0f, ((wordColor >> 5) & 0x1F) / 31.0f, (wordColor & 0x1F) / 31.0f, 1.0f ); } /// /// Check if property is sharpener property /// private bool IsSharpenerProperty(byte propType) { return propType >= 100 && propType <= 115; } /// /// Visualize float percent /// private int VisualizeFloatPercent(int value) { return value / 100; } /// /// Add one add-on property description /// private void AddOneAddOnPropDesc(int idProp, int[] param, int[] aPEEVals, int[] aRefines, bool local) { byte propType = GetPropertyType(idProp); if (!IsSharpenerProperty(propType)) { switch (propType) { case 0: if (!local && aPEEVals != null) aPEEVals[PEEI_PHYDAMAGE] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDPHYDAMAGE)); AddDescText(-1, true, " %+d", param[0]); break; case 1: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_MAXPHYDAMAGE), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXPHYDAMAGE), param[0]); } } else { if (aPEEVals != null) aPEEVals[PEEI_MAX_PHYDAMAGE] += param[0]; AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXPHYDAMAGE), param[0]); } break; case 2: AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDPHYDAMAGE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 3: if (!local && aPEEVals != null) aPEEVals[PEEI_MAGICDAMAGE] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMAGICDAMAGE)); AddDescText(-1, true, " %+d", param[0]); break; case 4: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_MAXMAGICDAMAGE), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXMAGICDAMAGE), param[0]); } } else { if (aPEEVals != null) aPEEVals[PEEI_MAX_MAGICDAMAGE] += param[0]; AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXMAGICDAMAGE), param[0]); } break; case 5: AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDMAGICDAMAGE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 6: if (!local && aPEEVals != null) { aPEEVals[PEEI_PHYDEF] += param[0]; aPEEVals[PEEI_PHYDAMAGE] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDPHYDAMAGE)); AddDescText(-1, true, " %+d", -param[1]); break; case 7: if (!local && aPEEVals != null) { aPEEVals[PEEI_PHYDAMAGE] += param[0]; aPEEVals[PEEI_PHYDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDPHYDAMAGE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); AddDescText(-1, true, " %+d", -param[1]); break; case 8: if (!local && aPEEVals != null) { aPEEVals[PEEI_MAGICDAMAGE] += param[0]; aPEEVals[PEEI_GOLDDEF] -= param[1]; aPEEVals[PEEI_WOODDEF] -= param[1]; aPEEVals[PEEI_WATERDEF] -= param[1]; aPEEVals[PEEI_FIREDEF] -= param[1]; aPEEVals[PEEI_EARTHDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMAGICDAMAGE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICDEF)); AddDescText(-1, true, " %+d", -param[1]); break; case 9: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKTIME), -BitConverter.ToSingle(BitConverter.GetBytes(param[0]), 0)); else AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKTIME), -param[0] * 0.05f); break; case 10: float dist = BitConverter.ToSingle(BitConverter.GetBytes(param[0]), 0); if (aPEEVals != null) { float cur = BitConverter.ToSingle(BitConverter.GetBytes(aPEEVals[PEEI_ATKDIST]), 0); cur += dist; aPEEVals[PEEI_ATKDIST] = BitConverter.ToInt32(BitConverter.GetBytes(cur), 0); } AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDATKDIST), dist); break; case 11: AddDescText(-1, true, GetItemDescString(ITEMDESC_CASTTIME), -(local ? VisualizeFloatPercent(param[0]) : param[0])); break; case 12: if (!local && aPEEVals != null) aPEEVals[PEEI_PHYDEF] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 13: AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYDEFENCE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 14: if (!local && aPEEVals != null) { aPEEVals[PEEI_GOLDDEF] += param[0]; aPEEVals[PEEI_WOODDEF] += param[0]; aPEEVals[PEEI_WATERDEF] += param[0]; aPEEVals[PEEI_FIREDEF] += param[0]; aPEEVals[PEEI_EARTHDEF] += param[0]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICDEF)); AddDescText(-1, true, " %+d", param[0]); break; case 15: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_GOLDDEFENCE)); if (param[0] != param[1]) AddDescText(-1, true, " %d~%d", param[0], param[1]); else AddDescText(-1, true, " %d", param[0]); } else { if (aPEEVals != null) aPEEVals[PEEI_GOLDDEF] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_GOLDDEFENCE)); AddDescText(-1, true, " %+d", param[0]); } break; case 16: AddDescText(-1, true, GetItemDescString(ITEMDESC_GOLDDEFENCE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 17: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_WOODDEFENCE)); if (param[0] != param[1]) AddDescText(-1, true, " %d~%d", param[0], param[1]); else AddDescText(-1, true, " %d", param[0]); } else { if (aPEEVals != null) aPEEVals[PEEI_WOODDEF] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_WOODDEFENCE)); AddDescText(-1, true, " %+d", param[0]); } break; case 18: AddDescText(-1, true, GetItemDescString(ITEMDESC_WOODDEFENCE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 19: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_WATERDEFENCE)); if (param[0] != param[1]) AddDescText(-1, true, " %d~%d", param[0], param[1]); else AddDescText(-1, true, " %d", param[0]); } else { if (aPEEVals != null) aPEEVals[PEEI_WATERDEF] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_WATERDEFENCE)); AddDescText(-1, true, " %+d", param[0]); } break; case 20: AddDescText(-1, true, GetItemDescString(ITEMDESC_WATERDEFENCE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 21: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_FIREDEFENCE)); if (param[0] != param[1]) AddDescText(-1, true, " %d~%d", param[0], param[1]); else AddDescText(-1, true, " %d", param[0]); } else { if (aPEEVals != null) aPEEVals[PEEI_FIREDEF] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_FIREDEFENCE)); AddDescText(-1, true, " %+d", param[0]); } break; case 22: AddDescText(-1, true, GetItemDescString(ITEMDESC_FIREDEFENCE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 23: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_EARTHDEFENCE)); if (param[0] != param[1]) AddDescText(-1, true, " %d~%d", param[0], param[1]); else AddDescText(-1, true, " %d", param[0]); } else { if (aPEEVals != null) aPEEVals[PEEI_EARTHDEF] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_EARTHDEFENCE)); AddDescText(-1, true, " %+d", param[0]); } break; case 24: AddDescText(-1, true, GetItemDescString(ITEMDESC_EARTHDEFENCE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 25: AddDescText(-1, true, GetItemDescString(ITEMDESC_GOLDDEFENCE) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, GetItemDescString(ITEMDESC_FIREDEFENCE) + " %", -VisualizeFloatPercent(param[1])); break; case 26: AddDescText(-1, true, GetItemDescString(ITEMDESC_WOODDEFENCE) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, GetItemDescString(ITEMDESC_GOLDDEFENCE) + " %", -VisualizeFloatPercent(param[1])); break; case 27: AddDescText(-1, true, GetItemDescString(ITEMDESC_WATERDEFENCE) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, GetItemDescString(ITEMDESC_EARTHDEFENCE) + " %", -VisualizeFloatPercent(param[1])); break; case 28: AddDescText(-1, true, GetItemDescString(ITEMDESC_FIREDEFENCE) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, GetItemDescString(ITEMDESC_WATERDEFENCE) + " %", -VisualizeFloatPercent(param[1])); break; case 29: AddDescText(-1, true, GetItemDescString(ITEMDESC_EARTHDEFENCE) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, GetItemDescString(ITEMDESC_WOODDEFENCE) + " %", -VisualizeFloatPercent(param[1])); break; case 30: if (!local && aPEEVals != null) { aPEEVals[PEEI_GOLDDEF] += param[0]; aPEEVals[PEEI_FIREDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_GOLDDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_FIREDEFENCE)); AddDescText(-1, true, " %+d", -param[1]); break; case 31: if (!local && aPEEVals != null) { aPEEVals[PEEI_WOODDEF] += param[0]; aPEEVals[PEEI_GOLDDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_WOODDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_GOLDDEFENCE)); AddDescText(-1, true, " %+d", -param[1]); break; case 32: if (!local && aPEEVals != null) { aPEEVals[PEEI_WATERDEF] += param[0]; aPEEVals[PEEI_EARTHDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_WATERDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_EARTHDEFENCE)); AddDescText(-1, true, " %+d", -param[1]); break; case 33: if (!local && aPEEVals != null) { aPEEVals[PEEI_FIREDEF] += param[0]; aPEEVals[PEEI_WATERDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_FIREDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_WATERDEFENCE)); AddDescText(-1, true, " %+d", -param[1]); break; case 34: if (!local && aPEEVals != null) { aPEEVals[PEEI_EARTHDEF] += param[0]; aPEEVals[PEEI_WOODDEF] -= param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_EARTHDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_WOODDEFENCE)); AddDescText(-1, true, " %+d", -param[1]); break; case 35: if (!local && aPEEVals != null) aPEEVals[PEEI_HP] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDHP)); AddDescText(-1, true, " %+d", param[0]); break; case 36: if (!local && aPEEVals != null) aPEEVals[PEEI_MP] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMP)); AddDescText(-1, true, " %+d", param[0]); break; case 37: AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDHP) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 38: AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDMP) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 39: AddDescText(-1, true, GetItemDescString(ITEMDESC_HPRECOVER), param[0] / 2); break; case 40: AddDescText(-1, true, GetItemDescString(ITEMDESC_MPRECOVER), param[0] / 2); break; case 41: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_STRENGTH), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_STRENGTH), param[0]); } } else AddDescText(-1, true, GetItemDescString(ITEMDESC_STRENGTH), param[0]); break; case 42: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_AGILITY), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_AGILITY), param[0]); } } else AddDescText(-1, true, GetItemDescString(ITEMDESC_AGILITY), param[0]); break; case 43: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ENERGY), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ENERGY), param[0]); } } else AddDescText(-1, true, GetItemDescString(ITEMDESC_ENERGY), param[0]); break; case 44: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_VITALITY), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_VITALITY), param[0]); } } else AddDescText(-1, true, GetItemDescString(ITEMDESC_VITALITY), param[0]); break; case 45: AddDescText(-1, true, GetItemDescString(ITEMDESC_DEADLYSTRIKE), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 46: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ATKRATING), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKRATING), param[0]); } } else AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKRATING), param[0]); break; case 47: AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKRATING) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 48: AddDescText(-1, true, GetItemDescString(ITEMDESC_RUNSPEED), BitConverter.ToSingle(BitConverter.GetBytes(param[0]), 0)); break; case 49: AddDescText(-1, true, GetItemDescString(ITEMDESC_RUNSPEEDEXTRA), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 50: if (!local && aPEEVals != null) aPEEVals[PEEI_DODGE] += param[0]; AddDescText(-1, false, GetItemDescString(ITEMDESC_DODGE)); AddDescText(-1, true, " %+d", param[0]); break; case 51: AddDescText(-1, true, GetItemDescString(ITEMDESC_DODGE) + " %", local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 52: AddDescText(-1, false, GetItemDescString(ITEMDESC_ENDURANCE)); AddDescText(-1, true, " %+d", param[0]); break; case 53: AddDescText(-1, true, GetItemDescString(ITEMDESC_ENDURANCE) + " %", VisualizeFloatPercent(param[0])); break; case 54: AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYRESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 55: AddDescText(-1, true, GetItemDescString(ITEMDESC_RANDOMPROP)); break; case 56: AddDescText(-1, true, GetItemDescString(ITEMDESC_REQEXTRA), -VisualizeFloatPercent(param[0])); break; case 57: AddDescText(-1, true, GetItemDescString(ITEMDESC_RANDOMPROP)); break; case 58: AddDescText(-1, true, GetItemDescString(ITEMDESC_EXP), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 59: AddDescText(-1, true, GetItemDescString(ITEMDESC_ATK_DEGREE), param[0]); break; case 60: AddDescText(-1, true, GetItemDescString(ITEMDESC_DEF_DEGREE), param[0]); break; case 61: AddDescText(-1, true, GetItemDescString(ITEMDESC_TOTAL_DEFENCE_ADD), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 62: AddDescText(-1, true, GetItemDescString(ITEMDESC_PROFVIEW)); break; case 63: AddDescText(-1, true, GetItemDescString(ITEMDESC_SOULPOWER), param[0]); break; case 64: AddDescText(-1, true, GetItemDescString(ITEMDESC_GOLDRESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 65: AddDescText(-1, true, GetItemDescString(ITEMDESC_WOODRESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 66: AddDescText(-1, true, GetItemDescString(ITEMDESC_WATERRESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 67: AddDescText(-1, true, GetItemDescString(ITEMDESC_FIRERESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 68: AddDescText(-1, true, GetItemDescString(ITEMDESC_EARTHRESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 69: AddDescText(-1, true, GetItemDescString(ITEMDESC_ALLMAGICRESIST), local ? VisualizeFloatPercent(param[0]) : param[0]); break; case 70: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ATK_DEGREE), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ATK_DEGREE), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ATK_DEGREE), param[0]); } break; case 71: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_DEF_DEGREE), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_DEF_DEGREE), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_DEF_DEGREE), param[0]); } break; case 72: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_DEADLYSTRIKE), VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d%%", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_DEADLYSTRIKE), VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_DEADLYSTRIKE), param[0]); } break; case 73: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDHP)); if (param[0] != param[1]) AddDescText(-1, true, " %+d~%d", param[0], param[1]); else AddDescText(-1, true, " %+d", param[0]); } else { AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDHP)); AddDescText(-1, true, " %+d", param[0]); } break; case 74: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMP)); if (param[0] != param[1]) AddDescText(-1, true, " %+d~%d", param[0], param[1]); else AddDescText(-1, true, " %+d", param[0]); } else { AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMP)); AddDescText(-1, true, " %+d", param[0]); } break; case 75: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ATKRATING) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKRATING) + " %", VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKRATING) + " %", param[0]); } break; case 76: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); if (param[0] != param[1]) AddDescText(-1, true, " %+d~%d", param[0], param[1]); else AddDescText(-1, true, " %+d", param[0]); } else { AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); AddDescText(-1, true, " %+d", param[0]); } break; case 77: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICDEF)); if (param[0] != param[1]) AddDescText(-1, true, " %+d~%d", param[0], param[1]); else AddDescText(-1, true, " %+d", param[0]); } else { AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICDEF)); AddDescText(-1, true, " %+d", param[0]); } break; case 78: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYRESIST), VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYRESIST), VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYRESIST), param[0]); } break; case 79: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICRESIST), VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ALLMAGICRESIST), VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ALLMAGICRESIST), param[0]); } break; case 80: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_CASTTIME), -VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d%%", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_CASTTIME), -VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_CASTTIME), -param[0]); } break; case 81: if (local) { float f0 = BitConverter.ToSingle(BitConverter.GetBytes(param[0]), 0); float f1 = BitConverter.ToSingle(BitConverter.GetBytes(param[1]), 0); if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDATKDIST), f0); AddDescText(-1, true, "~%.2f", f1); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDATKDIST), f0); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ADDATKDIST), BitConverter.ToSingle(BitConverter.GetBytes(param[0]), 0)); } break; case 82: if (local) { int h0 = param[0] / 2; int h1 = param[1] / 2; if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_MPRECOVER), h0); AddDescText(-1, true, "~%d", h1); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MPRECOVER), h0); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MPRECOVER), param[0] / 2); } break; case 83: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE) + " %", VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYDEFENCE) + " %", VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYDEFENCE) + " %", param[0]); } break; case 84: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_TOTAL_DEFENCE_ADD), VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_TOTAL_DEFENCE_ADD), VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_TOTAL_DEFENCE_ADD), param[0]); } break; case 85: if (local) { int hhp0 = param[0] / 2; int hhp1 = param[1] / 2; if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_HPRECOVER), hhp0); AddDescText(-1, true, "~%d", hhp1); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_HPRECOVER), hhp0); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_HPRECOVER), param[0] / 2); } break; case 86: if (local) { AddDescText(-1, false, GetItemDescString(ITEMDESC_DODGE)); if (param[0] != param[1]) AddDescText(-1, true, " %+d~%d", param[0], param[1]); else AddDescText(-1, true, " %+d", param[0]); } else { AddDescText(-1, false, GetItemDescString(ITEMDESC_DODGE)); AddDescText(-1, true, " %+d", param[0]); } break; case 87: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_MAXPHYDAMAGE), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXPHYDAMAGE), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXPHYDAMAGE), param[0]); } break; case 88: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_MAXMAGICDAMAGE), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXMAGICDAMAGE), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_MAXMAGICDAMAGE), param[0]); } break; case 89: if (local) { if (VisualizeFloatPercent(param[0]) != VisualizeFloatPercent(param[1])) { AddDescText(-1, false, GetItemDescString(ITEMDESC_REQEXTRA), -VisualizeFloatPercent(param[0])); AddDescText(-1, true, "~%d", VisualizeFloatPercent(param[1])); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_REQEXTRA), -VisualizeFloatPercent(param[0])); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_REQEXTRA), -VisualizeFloatPercent(param[0])); } break; case 90: AddDescText(-1, true, GetItemDescString(ITEMDESC_PENETRATION), param[0]); break; case 91: AddDescText(-1, true, GetItemDescString(ITEMDESC_RESILIENCE), param[0]); break; case 92: if (!local && aPEEVals != null) { aPEEVals[PEEI_PHYDEF] += param[0]; aPEEVals[PEEI_GOLDDEF] += param[1]; aPEEVals[PEEI_WOODDEF] += param[1]; aPEEVals[PEEI_WATERDEF] += param[1]; aPEEVals[PEEI_FIREDEF] += param[1]; aPEEVals[PEEI_EARTHDEF] += param[1]; } AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); AddDescText(-1, true, " %+d", param[0]); AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICDEF)); AddDescText(-1, true, " %+d", param[1]); break; case 93: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDHP)); AddDescText(-1, true, " %+d", param[0]); break; case 94: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDHP)); if (param[0] != param[1]) AddDescText(-1, true, " %+d~%d", param[0], param[1]); else AddDescText(-1, true, " %+d", param[0]); break; case 95: AddDescText(-1, true, GetItemDescString(ITEMDESC_STRENGTH), param[0]); break; case 96: AddDescText(-1, true, GetItemDescString(ITEMDESC_AGILITY), param[0]); break; case 97: AddDescText(-1, true, GetItemDescString(ITEMDESC_ENERGY), param[0]); break; case 98: AddDescText(-1, true, GetItemDescString(ITEMDESC_VITALITY), param[0]); break; case 99: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMP)); AddDescText(-1, true, " %+d", param[0]); break; case 120: AddDescText(-1, false, GetItemDescString(ITEMDESC_GOLDDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 121: AddDescText(-1, false, GetItemDescString(ITEMDESC_WOODDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 122: AddDescText(-1, false, GetItemDescString(ITEMDESC_WATERDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 123: AddDescText(-1, false, GetItemDescString(ITEMDESC_FIREDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 124: AddDescText(-1, false, GetItemDescString(ITEMDESC_EARTHDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 125: AddDescText(-1, true, GetItemDescString(ITEMDESC_ATKRATING), param[0]); break; case 126: AddDescText(-1, false, GetItemDescString(ITEMDESC_DODGE)); AddDescText(-1, true, " %+d", param[0]); break; case 127: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMP)); AddDescText(-1, true, " %+d", param[0]); break; case 128: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_VITALITY), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_VITALITY), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_VITALITY), param[0]); } break; case 129: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_STRENGTH), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_STRENGTH), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_STRENGTH), param[0]); } break; case 130: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_AGILITY), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_AGILITY), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_AGILITY), param[0]); } break; case 131: if (local) { if (param[0] != param[1]) { AddDescText(-1, false, GetItemDescString(ITEMDESC_ENERGY), param[0]); AddDescText(-1, true, "~%d", param[1]); } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ENERGY), param[0]); } } else { AddDescText(-1, true, GetItemDescString(ITEMDESC_ENERGY), param[0]); } break; case 132: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDHP)); AddDescText(-1, true, " %+d", param[0]); break; case 133: AddDescText(-1, false, GetItemDescString(ITEMDESC_PHYDEFENCE)); AddDescText(-1, true, " %+d", param[0]); break; case 134: AddDescText(-1, false, GetItemDescString(ITEMDESC_ALLMAGICDEF)); AddDescText(-1, true, " %+d", param[0]); break; case 135: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDPHYDAMAGE)); AddDescText(-1, true, " %+d", param[0]); break; case 136: AddDescText(-1, false, GetItemDescString(ITEMDESC_ADDMAGICDAMAGE)); AddDescText(-1, true, " %+d", param[0]); break; case 137: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_GOLDRESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_GOLDRESIST), param[0]); break; case 138: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_WOODRESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_WOODRESIST), param[0]); break; case 139: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_WATERRESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_WATERRESIST), param[0]); break; case 140: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_FIRERESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_FIRERESIST), param[0]); break; case 141: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_EARTHRESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_EARTHRESIST), param[0]); break; case 142: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_ALLMAGICRESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_ALLMAGICRESIST), param[0]); break; case 143: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_DEADLYSTRIKE), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_DEADLYSTRIKE), param[0]); break; case 144: AddDescText(-1, true, GetItemDescString(ITEMDESC_ATK_DEGREE), param[0]); break; case 145: AddDescText(-1, true, GetItemDescString(ITEMDESC_DEF_DEGREE), param[0]); break; case 146: if (local) AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYRESIST), VisualizeFloatPercent(param[0])); else AddDescText(-1, true, GetItemDescString(ITEMDESC_PHYRESIST), param[0]); break; case 160: AddDescText(-1, false, GetItemDescString(ITEMDESC_VIGOUR)); AddDescText(-1, true, " %+d", param[0]); break; case 200: if (aRefines != null) aRefines[REFINE_PHYDAMAGE] += param[0]; break; case 201: if (aRefines != null) aRefines[REFINE_MAGICDAMAGE] += param[0]; break; case 202: if (aRefines != null) aRefines[REFINE_PHYDEF] += param[0]; break; case 203: if (aRefines != null) aRefines[REFINE_GOLDDEF] += param[0]; break; case 204: if (aRefines != null) aRefines[REFINE_WOODDEF] += param[0]; break; case 205: if (aRefines != null) aRefines[REFINE_WATERDEF] += param[0]; break; case 206: if (aRefines != null) aRefines[REFINE_FIREDEF] += param[0]; break; case 207: if (aRefines != null) aRefines[REFINE_EARTHDEF] += param[0]; break; case 208: if (aRefines != null) aRefines[REFINE_HP] += param[0]; break; case 209: if (aRefines != null) aRefines[REFINE_DODGE] += param[0]; break; case 210: if (aRefines != null) { aRefines[REFINE_GOLDDEF] += param[0]; aRefines[REFINE_WOODDEF] += param[0]; aRefines[REFINE_WATERDEF] += param[0]; aRefines[REFINE_FIREDEF] += param[0]; aRefines[REFINE_EARTHDEF] += param[0]; } break; case 211: if (aRefines != null) { aRefines[REFINE_PHYDAMAGE] += param[0]; aRefines[REFINE_MAGICDAMAGE] += param[0]; } break; case 212: if (aRefines != null) { aRefines[REFINE_PHYDEF] += param[0]; aRefines[REFINE_GOLDDEF] += param[0]; aRefines[REFINE_WOODDEF] += param[0]; aRefines[REFINE_WATERDEF] += param[0]; aRefines[REFINE_FIREDEF] += param[0]; aRefines[REFINE_EARTHDEF] += param[0]; } break; default: AddDescText(-1, true, GetItemDescString(ITEMDESC_ERRORPROP), idProp); break; } } } /// /// Build add-ons properties description /// private void BuildAddOnPropDesc(int[] aPEEVals, int[] aRefines) { if (Props.Count == 0) return; // Change color m_strDesc += GetColorString(ITEMDESC_COL_LIGHTBLUE); foreach (Property prop in Props) { // Properties added by Embedded stone will be printed by BuildTesseraDesc() later // Ignore suite properties also if (prop.Embed || prop.Suite || prop.Engraved) continue; AddOneAddOnPropDesc(prop.Type, prop.Params, aPEEVals, aRefines, prop.Local); } } /// /// Get soul power added by this equipment /// public int GetSoulPowerAdded() { int added = 0; int propertyCount = Props.Count; foreach (Property prop in Props) { if (prop.NumParam > 0) { byte propType = GetPropertyType(prop.Type); if (propType == 63) // Soul power property { added += prop.Params[0]; } } } return added; } /// /// Build tessera description /// private void BuildTesseraDesc() { if (Holes.Count == 0) return; int cyanine = ITEMDESC_COL_CYANINE; foreach (int hole in Holes) { if (hole == 0) continue; // Get item name string itemName = EC_IvtrItem.ResolveItemName(hole); string descText = "null"; // This would normally check if it's a stone and get its description // For now, use the item name descText = itemName; AddDescText(cyanine, true, GetItemDescString(ITEMDESC_2STRINGS), itemName, descText); } } /// /// Add suite description /// public void AddSuiteDesc() { int idSuite = GetSuiteID(); if (idSuite == 0) return; // This equipment isn't one of any suite // This would normally build the suite description // For now, just add a placeholder m_strDesc += "\\r\\r"; AddDescText(ITEMDESC_COL_YELLOW, true, "Suite Equipment"); } /// /// Add destroying description /// public void AddDestroyingDesc(int dropItemID, int num) { if (!IsDestroying() || dropItemID == 0 || num == 0) return; // This would normally get the destroying info from element data int red = ITEMDESC_COL_RED; m_strDesc += "\\r"; AddDescText(red, true, GetItemDescString(ITEMDESC_EQUIP_DESTROYING)); // This would normally get the item name from element data AddDescText(red, true, GetItemDescString(ITEMDESC_EQUIP_REPAIR_NEED_ITEM), "Repair Item"); AddDescText(red, true, GetItemDescString(ITEMDESC_EQUIP_REPAIR_NEED_ITEMCNT), (int)(Math.Ceiling(num * 1.2))); } /// /// Add reputation requirement description /// public void AddReputationReqDesc() { if (ReputationReq > 0) { // This would normally check the host player's reputation int col = ITEMDESC_COL_WHITE; // Default to white, would check against player reputation AddDescText(col, true, GetItemDescString(ITEMDESC_REPUTATION_REQ), ReputationReq); } } /// /// Get engraved property number /// public int GetEngravedPropertyNum() { int num = 0; foreach (Property prop in Props) { if (prop.Engraved) num++; } return num; } /// /// Get preview info /// public string GetPreviewInfo() { m_strDesc = ""; BuildAddOnPropDesc(null, null); return m_strDesc; } /// /// Get add-on property description /// public string GetAddOnPropDesc(int num, int[] addons) { int[] aPEEVals = new int[MAX_PEEINDEX]; int[] aRefines = new int[MAX_REFINEINDEX]; m_strDesc = ""; if (Props.Count == 0) return m_strDesc; // Change color m_strDesc += GetColorString(ITEMDESC_COL_LIGHTBLUE); foreach (Property prop in Props) { if (prop.Embed || prop.Suite || prop.Engraved) continue; for (int j = 0; j < num; j++) { if ((addons[j] & 0x1fff) == prop.Type) { AddOneAddOnPropDesc(prop.Type, prop.Params, aPEEVals, aRefines, prop.Local); break; } } } return m_strDesc; } /// /// Get engrave description /// public string GetEngraveDesc() { int[] aPEEVals = new int[MAX_PEEINDEX]; int[] aRefines = new int[MAX_REFINEINDEX]; m_strDesc = ""; if (Props.Count == 0) return m_strDesc; // Change color m_strDesc += GetColorString(ITEMDESC_COL_YELLOW); foreach (Property prop in Props) { if (prop.Engraved) AddOneAddOnPropDesc(prop.Type, prop.Params, aPEEVals, aRefines, prop.Local); } return m_strDesc; } #endregion } /// /// Equipment addon description class /// public class EC_IvtrEquipAddonDesc { private object m_pAddon; private string m_strDesc = ""; /// /// Set addon /// public bool SetAddon(uint idEquipAddon) { if (idEquipAddon == 0) return false; // This would normally get the addon data from element data manager // For now, return false return false; } } }