4634 lines
191 KiB
C#
4634 lines
191 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using UnityEngine;
|
||
using UnityEngine.AddressableAssets;
|
||
using ModelRenderer.Scripts.GameData;
|
||
using ModelRenderer.Scripts.Common;
|
||
using BrewMonster;
|
||
using BrewMonster.Common;
|
||
using BrewMonster.Network;
|
||
using System.IO;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Reflection;
|
||
using BrewMonster.Scripts.Managers;
|
||
using BrewMonster.Scripts;
|
||
using UnityEngine.AddressableAssets;
|
||
using CSNetwork.Protocols;
|
||
using Unity.VisualScripting;
|
||
|
||
namespace PerfectWorld.Scripts.Managers
|
||
{
|
||
/// <summary>
|
||
/// Equipment item class that handles all equipment-specific functionality
|
||
/// Converted from C++ EC_IvtrEquip.cpp
|
||
/// </summary>
|
||
public class EC_IvtrEquip : EC_IvtrItem
|
||
{
|
||
#region Constants and Enums
|
||
|
||
|
||
public enum EQUIP_CLASS_ID
|
||
{
|
||
ICID_ITEM = -100,
|
||
ICID_EQUIP = -101,
|
||
ICID_ARMOR = 0,
|
||
ICID_ARMORRUNE,
|
||
ICID_ARROW,
|
||
ICID_DECORATION,
|
||
ICID_DMGRUNE,
|
||
ICID_ELEMENT,
|
||
ICID_FASHION,
|
||
ICID_FLYSWORD,
|
||
ICID_MATERIAL,
|
||
ICID_MEDICINE,
|
||
ICID_REVSCROLL,
|
||
ICID_SKILLTOME,
|
||
ICID_TOSSMAT,
|
||
ICID_TOWNSCROLL,
|
||
ICID_UNIONSCROLL,
|
||
ICID_WEAPON,
|
||
ICID_TASKITEM,
|
||
ICID_STONE,
|
||
ICID_WING,
|
||
ICID_TASKDICE,
|
||
ICID_TASKNMMATTER,
|
||
ICID_ERRORITEM,
|
||
ICID_FACETICKET,
|
||
ICID_FACEPILL,
|
||
ICID_GM_GENERATOR,
|
||
ICID_RECIPE,
|
||
ICID_PETEGG,
|
||
ICID_PETFOOD,
|
||
ICID_PETFACETICKET,
|
||
ICID_FIREWORK,
|
||
ICID_TANKCALLIN,
|
||
ICID_SKILLMATTER,
|
||
ICID_REFINETICKET,
|
||
ICID_DESTROYINGESSENCE,
|
||
ICID_BIBLE,
|
||
ICID_SPEAKER,
|
||
ICID_AUTOHP,
|
||
ICID_AUTOMP,
|
||
ICID_DOUBLEEXP,
|
||
ICID_TRANSMITSCROLL,
|
||
ICID_DYETICKET,
|
||
ICID_GOBLIN,
|
||
ICID_GOBLIN_EQUIP,
|
||
ICID_GOBLIN_EXPPILL,
|
||
ICID_CERTIFICATE,
|
||
ICID_TARGETITEM,
|
||
ICID_LOOKINFOITEM,
|
||
ICID_INCSKILLABILITY,
|
||
ICID_WEDDINGBOOKCARD,
|
||
ICID_WEDDINGINVITECARD,
|
||
ICID_SHARPENER,
|
||
ICID_FACTIONMATERIAL,
|
||
ICID_CONGREGATE,
|
||
ICID_FORCETOKEN,
|
||
ICID_DYNSKILLEQUIP,
|
||
ICID_MONEYCONVERTIBLE,
|
||
ICID_MONSTERSPIRIT,
|
||
ICID_GENERALCARD,
|
||
ICID_GENERALCARD_DICE,
|
||
ICID_SHOPTOKEN,
|
||
ICID_UNIVERSAL_TOKEN,
|
||
}
|
||
// Item Made From Types
|
||
public enum ITEM_MAKE_TAG
|
||
{
|
||
IMT_NULL,
|
||
IMT_CREATE, // GM ����
|
||
IMT_DROP, // �������
|
||
IMT_SHOP, // �̳ǻ��̵����
|
||
IMT_PRODUCE, // ������
|
||
IMT_SIGN, // װ��ǩ��
|
||
};
|
||
// 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 (must match C++ enum order)
|
||
public const int PEEI_PHYDAMAGE = 0;
|
||
public const int PEEI_PHYDEF = 1;
|
||
public const int PEEI_MAGICDAMAGE = 2;
|
||
public const int PEEI_GOLDDEF = 3;
|
||
public const int PEEI_WOODDEF = 4;
|
||
public const int PEEI_WATERDEF = 5;
|
||
public const int PEEI_FIREDEF = 6;
|
||
public const int PEEI_EARTHDEF = 7;
|
||
public const int PEEI_HP = 8;
|
||
public const int PEEI_MP = 9;
|
||
public const int PEEI_ENDURANCE = 10;
|
||
public const int PEEI_ATKDIST = 11;
|
||
public const int PEEI_STRENGTHREQ = 12;
|
||
public const int PEEI_AGILITYREQ = 13;
|
||
public const int PEEI_ATKSPEED = 14;
|
||
public const int PEEI_MAX_PHYDAMAGE = 15;
|
||
public const int PEEI_MAX_MAGICDAMAGE = 16;
|
||
public const int PEEI_DODGE = 17;
|
||
public const int MAX_PEEINDEX = 18;
|
||
|
||
// 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 and String IDs are now in DescriptipionMsg enum from EC_FixedMsg.cs
|
||
|
||
// Scale Types
|
||
public const int SCALE_SELL = 1;
|
||
|
||
// Endurance Scale
|
||
public const int ENDURANCE_SCALE = 100;
|
||
|
||
#endregion
|
||
|
||
#region Public Fields
|
||
|
||
// Basic Item Properties
|
||
public int TemplateId { get; set; }
|
||
public int ExpireDate { get; set; }
|
||
/// <summary>
|
||
/// class id
|
||
/// </summary>
|
||
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<int> Holes { get; set; }
|
||
public List<Property> Props { get; set; }
|
||
#endregion
|
||
|
||
|
||
|
||
#region Base Stats (from Element Data)
|
||
|
||
private static bool TryGetNumber<T>(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 void AddBaseStatsDesc(int[] aPEEVals, int[] aRefines)
|
||
{
|
||
elementdataman edm = ElementDataManProvider.GetElementDataMan();
|
||
if (edm == null) return;
|
||
|
||
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;
|
||
|
||
int white = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
int lblue = (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE;
|
||
|
||
// Try weapon-style fields first
|
||
int dmgLow = 0, dmgHigh = 0;
|
||
bool hasDmgLow = TryGetNumber<int>(data, new[] { "damage_low" }, out dmgLow);
|
||
bool hasDmgHigh = TryGetNumber<int>(data, new[] { "damage_high_max", "damage_high" }, out dmgHigh);
|
||
|
||
if (hasDmgLow && hasDmgHigh)
|
||
{
|
||
// Physical damage range - adjust by aPEEVals and aRefines (like C++: m_Essence.damage_low - aPEEVals[PEEI_PHYDAMAGE] + aRefines[REFINE_PHYDAMAGE])
|
||
int adjDmgLow = dmgLow - aPEEVals[PEEI_PHYDAMAGE] + aRefines[REFINE_PHYDAMAGE];
|
||
int adjDmgHigh = dmgHigh - aPEEVals[PEEI_PHYDAMAGE] - aPEEVals[PEEI_MAX_PHYDAMAGE] + aRefines[REFINE_PHYDAMAGE];
|
||
|
||
if (adjDmgLow > 0 || adjDmgHigh > 0 || aRefines[REFINE_PHYDAMAGE] != 0)
|
||
{
|
||
string dmgFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDAMAGE);
|
||
AddDescText(white, false, dmgFmt);
|
||
AddDescText(white, true, " {0}-{1}", adjDmgLow, adjDmgHigh);
|
||
}
|
||
|
||
// Magic damage if available
|
||
if (TryGetNumber<int>(data, new[] { "magic_damage_low" }, out int mdLow) &&
|
||
TryGetNumber<int>(data, new[] { "magic_damage_high_max", "magic_damage_high" }, out int mdHigh))
|
||
{
|
||
int adjMdLow = mdLow - aPEEVals[PEEI_MAGICDAMAGE] + aRefines[REFINE_MAGICDAMAGE];
|
||
int adjMdHigh = mdHigh - aPEEVals[PEEI_MAGICDAMAGE] - aPEEVals[PEEI_MAX_MAGICDAMAGE] + aRefines[REFINE_MAGICDAMAGE];
|
||
|
||
if (adjMdLow > 0 || adjMdHigh > 0 || aRefines[REFINE_MAGICDAMAGE] != 0)
|
||
{
|
||
string mdFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_MAGICDAMAGE);
|
||
AddDescText(white, false, mdFmt);
|
||
AddDescText(white, true, " {0}-{1}", adjMdLow, adjMdHigh);
|
||
}
|
||
}
|
||
|
||
// Attack time/speed - adjust color based on PEE_ATKSPEED
|
||
int atkCol = white;
|
||
if ((PropEffectEssence() & (1 << PEEI_ATKSPEED)) != 0)
|
||
atkCol = lblue;
|
||
|
||
if (TryGetNumber<float>(data, new[] { "attack_speed", "atk_speed" }, out float atkSpeed) && atkSpeed > 0)
|
||
{
|
||
// Show speed in attacks per second (1 / (attack_speed * 0.05))
|
||
float aps = 1.0f / (atkSpeed * 0.05f);
|
||
string atkFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_ATKSPEED);
|
||
if (atkFmt.Contains("%.2f") || atkFmt.Contains("%f"))
|
||
AddDescText(atkCol, true, atkFmt, aps);
|
||
else
|
||
AddDescText(atkCol, true, "{0} {1:F2}", atkFmt, aps);
|
||
}
|
||
|
||
// Range - adjust by aPEEVals[PEEI_ATKDIST]
|
||
float range;
|
||
if (TryGetNumber<float>(data, new[] { "range", "attack_range", "atk_range" }, out range))
|
||
{
|
||
float adjRange = range - BitConverter.ToSingle(BitConverter.GetBytes(aPEEVals[PEEI_ATKDIST]), 0);
|
||
string rangeFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_ATKDISTANCE);
|
||
if (rangeFmt.Contains("%") && (rangeFmt.Contains("f") || rangeFmt.Contains("d")))
|
||
AddDescText(white, true, rangeFmt, adjRange);
|
||
else
|
||
AddDescText(white, true, "{0} {1:F2}", rangeFmt, adjRange);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Try armor-style fields
|
||
if (TryGetNumber<int>(data, new[] { "defence_high", "defense_high", "defence", "defense" }, out int defHigh))
|
||
{
|
||
int adjDef = defHigh - aPEEVals[PEEI_PHYDEF] + aRefines[REFINE_PHYDEF];
|
||
if (adjDef > 0 || aRefines[REFINE_PHYDEF] != 0)
|
||
{
|
||
string defFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE);
|
||
AddDescText(white, false, defFmt);
|
||
AddDescText(white, true, " {0:+0;-#;+0}", adjDef);
|
||
}
|
||
}
|
||
|
||
// Dodge
|
||
if (TryGetNumber<int>(data, new[] { "armor", "dodge" }, out int dodge))
|
||
{
|
||
int adjDodge = dodge - aPEEVals[PEEI_DODGE] + aRefines[REFINE_DODGE];
|
||
if (adjDodge > 0 || aRefines[REFINE_DODGE] != 0)
|
||
{
|
||
string dodgeFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_DODGE);
|
||
AddDescText(white, false, dodgeFmt);
|
||
AddDescText(white, true, " {0:+0;-#;+0}", adjDodge);
|
||
}
|
||
}
|
||
|
||
// HP
|
||
if (TryGetNumber<int>(data, new[] { "hp_enhance", "hp" }, out int hp))
|
||
{
|
||
int adjHp = hp - aPEEVals[PEEI_HP] + aRefines[REFINE_HP];
|
||
if (adjHp > 0 || aRefines[REFINE_HP] != 0)
|
||
{
|
||
string hpFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_ADDHP);
|
||
AddDescText(white, false, hpFmt);
|
||
AddDescText(white, true, " {0:+0;-#;+0}", adjHp);
|
||
}
|
||
}
|
||
|
||
// MP
|
||
if (TryGetNumber<int>(data, new[] { "mp_enhance", "mp" }, out int mp))
|
||
{
|
||
int adjMp = mp - aPEEVals[PEEI_MP];
|
||
if (adjMp > 0)
|
||
{
|
||
string mpFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMP);
|
||
AddDescText(white, false, mpFmt);
|
||
AddDescText(white, true, " {0:+0;-#;+0}", adjMp);
|
||
}
|
||
}
|
||
|
||
// Elemental resistances - adjust by aPEEVals and aRefines
|
||
// In C++: m_Essence.resistance[MAGICCLASS_GOLD] - aPEEVals[PEEI_GOLDDEF] + aRefines[REFINE_GOLDDEF]
|
||
// In C# struct: resistance is stored in magic_defences array
|
||
(DescriptipionMsg descId, int refineIdx, int peeIdx, int magicClassIdx)[] res = new (DescriptipionMsg, int, int, int)[]
|
||
{
|
||
(DescriptipionMsg.ITEMDESC_GOLDDEFENCE, REFINE_GOLDDEF, PEEI_GOLDDEF, 0), // MAGICCLASS_GOLD = 0
|
||
(DescriptipionMsg.ITEMDESC_WOODDEFENCE, REFINE_WOODDEF, PEEI_WOODDEF, 1), // MAGICCLASS_WOOD = 1
|
||
(DescriptipionMsg.ITEMDESC_WATERDEFENCE, REFINE_WATERDEF, PEEI_WATERDEF, 2), // MAGICCLASS_WATER = 2
|
||
(DescriptipionMsg.ITEMDESC_FIREDEFENCE, REFINE_FIREDEF, PEEI_FIREDEF, 3), // MAGICCLASS_FIRE = 3
|
||
(DescriptipionMsg.ITEMDESC_EARTHDEFENCE, REFINE_EARTHDEF, PEEI_EARTHDEF, 4) // MAGICCLASS_EARTH = 4
|
||
};
|
||
|
||
// Try to read from magic_defences array first (C# struct format)
|
||
bool foundResistanceArray = false;
|
||
try
|
||
{
|
||
var dataType = data.GetType();
|
||
var magicDefencesField = dataType.GetField("magic_defences");
|
||
if (magicDefencesField != null)
|
||
{
|
||
var magicDefences = magicDefencesField.GetValue(data);
|
||
if (magicDefences != null && magicDefences is Array)
|
||
{
|
||
foundResistanceArray = true;
|
||
Array arr = (Array)magicDefences;
|
||
for (int i = 0; i < res.Length && i < arr.Length; i++)
|
||
{
|
||
var magicDef = arr.GetValue(i);
|
||
if (magicDef != null)
|
||
{
|
||
var highField = magicDef.GetType().GetField("high");
|
||
if (highField != null)
|
||
{
|
||
int v = Convert.ToInt32(highField.GetValue(magicDef));
|
||
var descId = res[i].descId;
|
||
var refineIdx = res[i].refineIdx;
|
||
var peeIdx = res[i].peeIdx;
|
||
|
||
int adjV = v - aPEEVals[peeIdx] + aRefines[refineIdx];
|
||
if (adjV > 0 || aRefines[refineIdx] != 0)
|
||
{
|
||
string resFmt = GetItemDescString(descId);
|
||
AddDescText(white, false, resFmt);
|
||
AddDescText(white, true, " {0:+0;-#;+0}", adjV);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// Fallback: try reading from individual fields if array method didn't work
|
||
if (!foundResistanceArray)
|
||
{
|
||
for (int i = 0; i < res.Length; i++)
|
||
{
|
||
var descId = res[i].descId;
|
||
var refineIdx = res[i].refineIdx;
|
||
var peeIdx = res[i].peeIdx;
|
||
var magicClassIdx = res[i].magicClassIdx;
|
||
|
||
// Try multiple field name patterns
|
||
string[] names = new[]
|
||
{
|
||
$"magic_defences[{magicClassIdx}].high",
|
||
$"magic_defences_{magicClassIdx}_high",
|
||
$"resistance_{magicClassIdx}",
|
||
$"resist_{magicClassIdx}",
|
||
"resist_metal", "resistance_metal", "gold_resist" // For index 0
|
||
};
|
||
|
||
if (TryGetNumber<int>(data, names, out int v))
|
||
{
|
||
int adjV = v - aPEEVals[peeIdx] + aRefines[refineIdx];
|
||
if (adjV > 0 || aRefines[refineIdx] != 0)
|
||
{
|
||
string resFmt = GetItemDescString(descId);
|
||
AddDescText(white, false, resFmt);
|
||
AddDescText(white, true, " {0:+0;-#;+0}", adjV);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// Keep old method for backward compatibility
|
||
private string GetBaseStatsDesc()
|
||
{
|
||
string oldDesc = m_strDesc;
|
||
m_strDesc = "";
|
||
int[] emptyPEE = new int[MAX_PEEINDEX];
|
||
int[] emptyRefines = new int[MAX_REFINEINDEX];
|
||
AddBaseStatsDesc(emptyPEE, emptyRefines);
|
||
string result = m_strDesc;
|
||
m_strDesc = oldDesc;
|
||
return result;
|
||
}
|
||
|
||
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) : base(tid, expireDate)
|
||
{
|
||
TemplateId = tid;
|
||
ExpireDate = expireDate;
|
||
CID = (int)InventoryClassId.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<int>();
|
||
Props = new List<Property>();
|
||
}
|
||
|
||
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<int>(other.Holes);
|
||
Props = new List<Property>(other.Props.Select(p => new Property(p)));
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Core Methods
|
||
|
||
/// <summary>
|
||
/// Set item detail information from binary data
|
||
/// </summary>
|
||
public override bool SetItemInfo(byte[] infoData, int dataLen)
|
||
{
|
||
base.SetItemInfo(infoData, 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;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private bool TryParseEquipInfoNative(byte[] data, int len)
|
||
{
|
||
try
|
||
{
|
||
CECDataReader dr = new CECDataReader(data, len);
|
||
LevelReq = dr.ReadShort();
|
||
ProfReq = dr.ReadShort();
|
||
StrengthReq= dr.ReadShort();
|
||
VitalityReq= dr.ReadShort();
|
||
AgilityReq = dr.ReadShort();
|
||
EnergyReq = dr.ReadShort();
|
||
CurEndurance = dr.ReadInt();
|
||
MaxEndurance = dr.ReadInt();
|
||
int essenceSize = dr.ReadShort();
|
||
ReadMakerInfo(dr);
|
||
dr.Offset(essenceSize, CECDataReader.SEEK_CUR);
|
||
if(essenceSize < 0 )
|
||
{
|
||
throw new Exception("TYPE_DATAERR");
|
||
}
|
||
|
||
int numHole = dr.ReadShort();
|
||
StoneMask = (ushort)dr.ReadShort();
|
||
if (numHole > 0)
|
||
{
|
||
Holes.Clear();
|
||
Holes.Capacity = numHole;
|
||
for (int i = 0; i < numHole; i++)
|
||
{
|
||
Holes.Add(dr.ReadInt());
|
||
}
|
||
}
|
||
else if (numHole == 0)
|
||
{
|
||
Holes.Clear();
|
||
}
|
||
else
|
||
{
|
||
throw new Exception("TYPE_DATAERR");
|
||
}
|
||
int numProp = dr.ReadInt();
|
||
if (numProp > 0)
|
||
{
|
||
Props.Clear();
|
||
Props.Capacity = numProp;
|
||
for (int i = 0; i < numProp; i++)
|
||
{
|
||
int type = dr.ReadInt();
|
||
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++)
|
||
{
|
||
prop.Params[j] = dr.ReadInt();
|
||
}
|
||
Props.Add(prop);
|
||
}
|
||
}
|
||
else if (numProp == 0)
|
||
{
|
||
Props.Clear();
|
||
}
|
||
else
|
||
{
|
||
throw new Exception("TYPE_DATAERR");
|
||
}
|
||
|
||
// Sanity check to catch misalignment
|
||
if (LevelReq < 0 || LevelReq > 2000) return false;
|
||
if (MaxEndurance < 0 || MaxEndurance > 1000000) return false;
|
||
|
||
return true;
|
||
}
|
||
catch (System.Exception ex) {
|
||
BMLogger.LogError("CECIvtrEquip::SetItemInfo, data read error (" + ex.GetType() + ")" + ex.StackTrace);
|
||
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; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Read maker information from binary data
|
||
/// </summary>
|
||
protected void ReadMakerInfo(CECDataReader dr)
|
||
{
|
||
// Debug: Log the bytes at current position before reading
|
||
// We need to check what bytes are actually at the reader position
|
||
MadeFrom = dr.ReadByte();
|
||
int makerLen = dr.ReadByte();
|
||
if (makerLen > 0)
|
||
{
|
||
if (MadeFrom == (byte)ITEM_MAKE_TAG.IMT_SIGN)
|
||
{
|
||
ushort color = (ushort)dr.ReadShort();
|
||
makerLen -= sizeof(ushort);
|
||
|
||
byte[] makerData = dr.ReadData(makerLen);
|
||
// Find null terminator (0x00 0x00 for Unicode) and decode only up to that point
|
||
int actualLength = makerLen;
|
||
for (int i = 0; i < makerLen - 1; i += 2)
|
||
{
|
||
if (makerData[i] == 0 && makerData[i + 1] == 0)
|
||
{
|
||
actualLength = i;
|
||
break;
|
||
}
|
||
}
|
||
string maker = System.Text.Encoding.Unicode.GetString(makerData, 0, actualLength).TrimEnd('\0');
|
||
if (string.IsNullOrEmpty(maker))
|
||
{
|
||
return;
|
||
}
|
||
//#define FASHION_WORDCOLOR_TO_A3DCOLOR(c) A3DCOLORRGB(((c) & (0x1f << 10)) >> 7, ((c) & (0x1f << 5)) >> 2, ((c) & 0x1f) << 3)
|
||
//A3DCOLOR clr = FASHION_WORDCOLOR_TO_A3DCOLOR(color);
|
||
Color clr = ColorFromWord(color);
|
||
SetNewMark(maker, clr);
|
||
}
|
||
else
|
||
{
|
||
//m_strMaker = ACString((ACHAR*)dr.Read_Data(iMakerLen), iMakerLen / sizeof (ACHAR));
|
||
byte[] makerData = dr.ReadData(makerLen);
|
||
// Find null terminator (0x00 0x00 for Unicode) and decode only up to that point
|
||
int actualLength = makerLen;
|
||
for (int i = 0; i < makerLen - 1; i += 2)
|
||
{
|
||
if (makerData[i] == 0 && makerData[i + 1] == 0)
|
||
{
|
||
actualLength = i;
|
||
break;
|
||
}
|
||
}
|
||
Maker = System.Text.Encoding.Unicode.GetString(makerData, 0, actualLength).TrimEnd('\0');
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Maker = "";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Set new mark with color
|
||
/// </summary>
|
||
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(DescriptipionMsg.ITEMDESC_EQUIPMARK), Maker);
|
||
}
|
||
MadeFrom = string.IsNullOrEmpty(mark) ? (byte)ITEM_MAKE_TAG.IMT_NULL : (byte)ITEM_MAKE_TAG.IMT_SIGN;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Utility Methods
|
||
|
||
/// <summary>
|
||
/// Get item name
|
||
/// </summary>
|
||
public virtual string GetName()
|
||
{
|
||
return EC_IvtrItemUtils.Instance.ResolveItemName(TemplateId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add current endurance
|
||
/// </summary>
|
||
public int AddCurEndurance(int value)
|
||
{
|
||
CurEndurance += value;
|
||
CurEndurance = Mathf.Clamp(CurEndurance, 0, MaxEndurance);
|
||
return CurEndurance;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Convert endurance real value to displaying value
|
||
/// </summary>
|
||
public static int VisualizeEndurance(int v)
|
||
{
|
||
return (v + ENDURANCE_SCALE - 1) / ENDURANCE_SCALE;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get empty hole number
|
||
/// </summary>
|
||
public int GetEmptyHoleNum()
|
||
{
|
||
int count = 0;
|
||
foreach (int hole in Holes)
|
||
{
|
||
if (hole == 0)
|
||
count++;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get repair cost
|
||
/// </summary>
|
||
public int GetRepairCost()
|
||
{
|
||
if (MaxEndurance == 0 || MaxEndurance == CurEndurance)
|
||
return 0;
|
||
|
||
int cost = (int)GetRawRepairCost();
|
||
if (cost < 1)
|
||
cost = 1;
|
||
|
||
return cost;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get raw repair cost
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get scaled item price
|
||
/// </summary>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check if item is repairable
|
||
/// </summary>
|
||
public bool IsRepairable()
|
||
{
|
||
return MaxEndurance > 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check if item is destroying
|
||
/// </summary>
|
||
public bool IsDestroying()
|
||
{
|
||
return CurEndurance == 0 && MaxEndurance > 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check if item is rare
|
||
/// </summary>
|
||
public virtual bool IsRare()
|
||
{
|
||
return RefineLvl >= 3;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Property System
|
||
|
||
/// <summary>
|
||
/// Get property effect essence flags
|
||
/// </summary>
|
||
public uint PropEffectEssence()
|
||
{
|
||
uint flags = 0;
|
||
foreach (Property prop in Props)
|
||
{
|
||
flags = PropEffectMask(prop, flags);
|
||
}
|
||
return flags;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get property effect mask for a specific property
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get property type from property ID
|
||
/// </summary>
|
||
private static Dictionary<int, byte> 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<int, byte>();
|
||
|
||
try
|
||
{
|
||
// Load item_ext_prop.txt from Addressables
|
||
// Address must match the Addressables "Address" configured in Assets/AddressableAssetsData/...
|
||
const string address = "Assets/Addressable/item_ext_prop.txt";
|
||
|
||
// Initialize Addressables if not already initialized
|
||
Addressables.InitializeAsync().WaitForCompletion();
|
||
|
||
// Load the TextAsset synchronously (since GetPropertyType is called synchronously)
|
||
var textAsset = Addressables.LoadAssetAsync<TextAsset>(address).WaitForCompletion();
|
||
|
||
if (textAsset != null && !string.IsNullOrEmpty(textAsset.text))
|
||
{
|
||
// Parse the text content
|
||
int currentType = -1;
|
||
bool inTypeBlock = false;
|
||
bool inBlockComment = false;
|
||
|
||
using (var reader = new StringReader(textAsset.text))
|
||
{
|
||
string rawLine;
|
||
while ((rawLine = reader.ReadLine()) != null)
|
||
{
|
||
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*(?<type>\\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(?<id>\\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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"[EC_IvtrEquip] Failed to load item_ext_prop.txt from Addressables (address: {address})");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"[EC_IvtrEquip] Exception loading item_ext_prop.txt from Addressables: {ex.Message}");
|
||
// ignore parse errors; fallback below will handle unknown properties
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Set properties to local
|
||
/// </summary>
|
||
public void SetLocalProps()
|
||
{
|
||
if (Props.Count == 0)
|
||
return;
|
||
|
||
foreach (Property prop in Props)
|
||
{
|
||
prop.Local = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get deadly strike rate provided by this equipment
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Decide equipment name color
|
||
/// </summary>
|
||
protected override int DecideNameCol()
|
||
{
|
||
int index = GetColorStrID(TemplateId);
|
||
if (index >= 0)
|
||
return index;
|
||
|
||
int col = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
|
||
switch (FixProps)
|
||
{
|
||
case 1: col = (int)DescriptipionMsg.ITEMDESC_COL_GREEN; break;
|
||
case 2: col = (int)DescriptipionMsg.ITEMDESC_COL_YELLOW; break;
|
||
case 3: col = (int)DescriptipionMsg.ITEMDESC_COL_DARKGOLD; break;
|
||
default:
|
||
if (PropNum > 0)
|
||
col = (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE;
|
||
break;
|
||
}
|
||
|
||
return col;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get color string ID for template
|
||
/// </summary>
|
||
public override int GetColorStrID(int templateId)
|
||
{
|
||
// This would normally query the game's color system
|
||
int iIndex = EC_Game.GetItemNameColorIdx(templateId);
|
||
if (iIndex <= 0)
|
||
return -1;
|
||
else if (iIndex < 7)
|
||
return (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE + iIndex - 1;
|
||
else
|
||
return (int)DescriptipionMsg.ITEMDESC_COL2_START + iIndex - 7 + 1;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Description Methods (high level entry points)
|
||
|
||
/// <summary>
|
||
/// Get normal in-inventory description, mirroring C++ CECIvtrEquip::GetNormalDesc.
|
||
/// This is a single formatted string using ^color codes and '\\r' as line separators.
|
||
/// </summary>
|
||
protected override string GetNormalDesc(bool bRepair)
|
||
{
|
||
// Build addon and refine properties and save it (like C++ does first)
|
||
int[] aPEEVals = new int[MAX_PEEINDEX];
|
||
int[] aRefines = new int[MAX_REFINEINDEX];
|
||
for (int i = 0; i < MAX_PEEINDEX; i++)
|
||
aPEEVals[i] = 0;
|
||
for (int i = 0; i < MAX_REFINEINDEX; i++)
|
||
aRefines[i] = 0;
|
||
|
||
m_strDesc = "";
|
||
BuildAddOnPropDesc(aPEEVals, aRefines);
|
||
string strAddon = m_strDesc;
|
||
|
||
// Reset and build description from scratch
|
||
m_strDesc = "";
|
||
|
||
int white = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
int lblue = (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE;
|
||
int red = (int)DescriptipionMsg.ITEMDESC_COL_RED;
|
||
|
||
// 1) Item name
|
||
AddDescText(white, true, GetItemDescString(DescriptipionMsg.ITEMDESC_NAME), GetName());
|
||
|
||
// 1.5) Sub class name (loại trang bị) - like C++: AddDescText(white, true, pDescTab->GetWideString(ITEMDESC_CLASSNAME), m_pDBSubType->name);
|
||
// In C++, this is always called (no null check), so we always call it too
|
||
string subTypeName = GetSubTypeName();
|
||
AddDescText(white, true, GetItemDescString(DescriptipionMsg.ITEMDESC_CLASSNAME), subTypeName ?? "");
|
||
|
||
// 1.6) Item level (cấp vũ khí/trang bị) - like C++: AddDescText(-1, true, pDescTab->GetWideString(ITEMDESC_LEVEL), m_Essence.weapon_level);
|
||
int itemLevel = GetItemLevel();
|
||
if (itemLevel > 0)
|
||
{
|
||
AddDescText(-1, true, GetItemDescString(DescriptipionMsg.ITEMDESC_LEVEL), itemLevel);
|
||
}
|
||
|
||
// 2) Base stats from element data (damage/defence/speed/range/resists)
|
||
// Adjust base stats by subtracting aPEEVals and adding aRefines
|
||
AddBaseStatsDesc(aPEEVals, aRefines);
|
||
|
||
// 3) Endurance (current / max) - adjust color based on PEE_ENDURANCE
|
||
if (MaxEndurance > 0)
|
||
{
|
||
int col = white;
|
||
if (CurEndurance == 0)
|
||
col = red;
|
||
else if ((PropEffectEssence() & (1 << PEEI_ENDURANCE)) != 0)
|
||
col = lblue;
|
||
|
||
int curVis = VisualizeEndurance(CurEndurance);
|
||
int maxVis = VisualizeEndurance(MaxEndurance);
|
||
AddDescText(col, true, "{0} {1}/{2}",
|
||
GetItemDescString(DescriptipionMsg.ITEMDESC_ENDURANCE), curVis, maxVis);
|
||
}
|
||
|
||
// 4) Requirements (level / stats / reputation)
|
||
AddRequirementDesc(aPEEVals);
|
||
AddReputationReqDesc();
|
||
|
||
// 4.5) Profession restriction (phái hạn chế) - like C++: AddProfReqDesc(m_iProfReq);
|
||
// In C++, this is always called regardless of value (the function checks internally)
|
||
AddProfReqDesc(ProfReq);
|
||
|
||
// 5) Add-on properties (non-embedded, non-suite, non-engraved)
|
||
if (!string.IsNullOrEmpty(strAddon))
|
||
m_strDesc += strAddon;
|
||
AddPriceDesc(white, false);
|
||
// 6) Tessera / stones (socketed gems)
|
||
BuildTesseraDesc();
|
||
|
||
// 7) Sharpener properties
|
||
AddSharpenerDesc();
|
||
|
||
// 8) Engraved properties
|
||
AddEngravedDesc();
|
||
|
||
// 9) Suite information
|
||
AddSuiteDesc();
|
||
|
||
// 10) Maker & destroying info if any
|
||
AddMakerDesc();
|
||
// Destroying description is added by caller when needed; keep it optional here.
|
||
m_strDesc += "\\r";
|
||
AddExtDescText();
|
||
Debug.Log("m_strDesc add ext desc text: " + m_strDesc);
|
||
// 11) Price (sell price scaled)
|
||
return m_strDesc;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get item description for booth buying
|
||
/// </summary>
|
||
public string GetBoothBuyDesc()
|
||
{
|
||
m_strDesc = "";
|
||
|
||
int white = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
|
||
// Item name
|
||
AddDescText(white, true, GetItemDescString(DescriptipionMsg.ITEMDESC_NAME), GetName());
|
||
|
||
// Base stats from element data
|
||
string baseStats = GetBaseStatsDesc();
|
||
if (!string.IsNullOrEmpty(baseStats))
|
||
{
|
||
m_strDesc += GetColorString(DescriptipionMsg.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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add concise requirement description (level / stats / profession).
|
||
/// This is a simplified mirror of the original C++ text; colors are kept white for now.
|
||
/// </summary>
|
||
private void AddRequirementDesc(int[] aPEEVals)
|
||
{
|
||
int white = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
int lblue = (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE;
|
||
|
||
// Get host player for requirement checks (via GameRun if available)
|
||
object hostPlayer = null;
|
||
try
|
||
{
|
||
var gameRun = EC_Game.GetGameRun();
|
||
if (gameRun != null)
|
||
{
|
||
// Try to get host player - method name may vary
|
||
hostPlayer = gameRun; // Placeholder - would need actual GetHostPlayer() method
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
uint dwPEE = PropEffectEssence();
|
||
|
||
// Level requirement
|
||
if (LevelReq > 0)
|
||
{
|
||
int col = white;
|
||
if (hostPlayer != null)
|
||
{
|
||
// In C++: pHost->GetMaxLevelSofar() >= m_iLevelReq ? white : red
|
||
// For now, use a simple check - would need GetMaxLevelSofar() equivalent
|
||
col = white; // TODO: Check actual player level
|
||
}
|
||
|
||
string levelFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_LEVELREQ);
|
||
if (levelFmt.Contains("%d"))
|
||
AddDescText(col, true, levelFmt, LevelReq);
|
||
else
|
||
AddDescText(col, true, "{0} {1}", levelFmt, LevelReq);
|
||
}
|
||
|
||
// Stat requirements - adjust color based on PEE flags and player stats
|
||
if (StrengthReq > 0)
|
||
{
|
||
int col = white;
|
||
if (hostPlayer != null)
|
||
{
|
||
// In C++: pHost->GetExtendProps().bs.strength < m_iStrengthReq ? red : ((dwPEE & PEE_STRENGTHREQ) ? lblue : white)
|
||
// For now, use simple check
|
||
if ((dwPEE & (1 << PEEI_STRENGTHREQ)) != 0)
|
||
col = lblue;
|
||
// TODO: Check actual player strength
|
||
}
|
||
|
||
string strFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTHREQ);
|
||
if (strFmt.Contains("%d"))
|
||
AddDescText(col, true, strFmt, StrengthReq);
|
||
else
|
||
AddDescText(col, true, "{0} {1}", strFmt, StrengthReq);
|
||
}
|
||
if (AgilityReq > 0)
|
||
{
|
||
int col = white;
|
||
if (hostPlayer != null)
|
||
{
|
||
if ((dwPEE & (1 << PEEI_AGILITYREQ)) != 0)
|
||
col = lblue;
|
||
// TODO: Check actual player agility
|
||
}
|
||
|
||
string agiFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITYREQ);
|
||
if (agiFmt.Contains("%d"))
|
||
AddDescText(col, true, agiFmt, AgilityReq);
|
||
else
|
||
AddDescText(col, true, "{0} {1}", agiFmt, AgilityReq);
|
||
}
|
||
if (VitalityReq > 0)
|
||
{
|
||
int col = white;
|
||
if (hostPlayer != null)
|
||
{
|
||
// TODO: Check PEE_VITALITYREQ flag and player vitality
|
||
}
|
||
|
||
string vitFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITYREQ);
|
||
if (vitFmt.Contains("%d"))
|
||
AddDescText(col, true, vitFmt, VitalityReq);
|
||
else
|
||
AddDescText(col, true, "{0} {1}", vitFmt, VitalityReq);
|
||
}
|
||
if (EnergyReq > 0)
|
||
{
|
||
int col = white;
|
||
if (hostPlayer != null)
|
||
{
|
||
// TODO: Check PEE_ENERGYREQ flag and player energy
|
||
}
|
||
|
||
string eneFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGYREQ);
|
||
if (eneFmt.Contains("%d"))
|
||
AddDescText(col, true, eneFmt, EnergyReq);
|
||
else
|
||
AddDescText(col, true, "{0} {1}", eneFmt, EnergyReq);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get sub type name (loại trang bị) from element data
|
||
/// </summary>
|
||
private string GetSubTypeName()
|
||
{
|
||
elementdataman edm = ElementDataManProvider.GetElementDataMan();
|
||
if (edm == null) return null;
|
||
|
||
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 null;
|
||
|
||
// Get id_sub_type from essence
|
||
uint idSubType = 0;
|
||
if (TryGetNumber<uint>(data, new[] { "id_sub_type" }, out idSubType) && idSubType > 0)
|
||
{
|
||
// Try to get sub type data - check if it's weapon or armor
|
||
DATA_TYPE subTypeDt = DATA_TYPE.DT_INVALID;
|
||
object subTypeData = null;
|
||
|
||
// Try weapon sub type first
|
||
subTypeData = edm.get_data_ptr(idSubType, ID_SPACE.ID_SPACE_ESSENCE, ref subTypeDt);
|
||
if (subTypeDt == DATA_TYPE.DT_WEAPON_SUB_TYPE)
|
||
{
|
||
var nameField = subTypeData.GetType().GetField("name");
|
||
if (nameField != null)
|
||
{
|
||
var nameArray = nameField.GetValue(subTypeData);
|
||
if (nameArray != null && nameArray is ushort[])
|
||
{
|
||
return ByteToStringUtils.UshortArrayToUnicodeString((ushort[])nameArray);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Try armor sub type
|
||
subTypeDt = DATA_TYPE.DT_INVALID;
|
||
subTypeData = edm.get_data_ptr(idSubType, ID_SPACE.ID_SPACE_ESSENCE, ref subTypeDt);
|
||
if (subTypeDt == DATA_TYPE.DT_ARMOR_SUB_TYPE)
|
||
{
|
||
var nameField = subTypeData.GetType().GetField("name");
|
||
if (nameField != null)
|
||
{
|
||
var nameArray = nameField.GetValue(subTypeData);
|
||
if (nameArray != null && nameArray is ushort[])
|
||
{
|
||
return ByteToStringUtils.UshortArrayToUnicodeString((ushort[])nameArray);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get item level (cấp vũ khí/trang bị) from element data
|
||
/// </summary>
|
||
private int GetItemLevel()
|
||
{
|
||
elementdataman edm = ElementDataManProvider.GetElementDataMan();
|
||
if (edm == null) return 0;
|
||
|
||
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 0;
|
||
|
||
// Try weapon_level first (for weapons)
|
||
if (TryGetNumber<int>(data, new[] { "weapon_level" }, out int weaponLevel) && weaponLevel > 0)
|
||
return weaponLevel;
|
||
|
||
// Try level (for armor and other items)
|
||
if (TryGetNumber<int>(data, new[] { "level" }, out int level) && level > 0)
|
||
return level;
|
||
}
|
||
catch { }
|
||
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get character combo ID (phái hạn chế) from element data
|
||
/// </summary>
|
||
private uint GetCharacterComboId()
|
||
{
|
||
elementdataman edm = ElementDataManProvider.GetElementDataMan();
|
||
if (edm == null) return 0;
|
||
|
||
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 0;
|
||
|
||
if (TryGetNumber<uint>(data, new[] { "character_combo_id" }, out uint comboId))
|
||
return comboId;
|
||
}
|
||
catch { }
|
||
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add profession requirement description (phái hạn chế)
|
||
/// Like C++: AddProfReqDesc(int iProfReq) - displays class restrictions
|
||
/// </summary>
|
||
private void AddProfReqDesc(int profReq)
|
||
{
|
||
// In C++: if (CECProfConfig::Instance().ContainsAllProfession(iProfReq)) return;
|
||
// Check if all professions are allowed - if so, don't display restriction
|
||
// If profReq == 0, it means no restriction (all professions allowed)
|
||
// If all 12 bits are set (0xFFF = 4095), it also means all professions allowed
|
||
if (profReq == 0)
|
||
return; // No restriction
|
||
|
||
const int NUM_PROFESSION = 12;
|
||
const int allProfMask = (1 << NUM_PROFESSION) - 1; // 0xFFF = 4095 (all 12 bits set)
|
||
if ((profReq & allProfMask) == allProfMask)
|
||
return; // All professions allowed
|
||
|
||
// Check if any profession bit is set - if none, don't display
|
||
bool hasAnyProf = false;
|
||
for (int i = 0; i < NUM_PROFESSION; i++)
|
||
{
|
||
if ((profReq & (1 << i)) != 0)
|
||
{
|
||
hasAnyProf = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!hasAnyProf)
|
||
return; // No profession restriction specified
|
||
|
||
int white = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
int red = (int)DescriptipionMsg.ITEMDESC_COL_RED;
|
||
|
||
// Get host player profession for color check
|
||
int playerProf = -1;
|
||
try
|
||
{
|
||
var gameRun = EC_Game.GetGameRun();
|
||
if (gameRun != null)
|
||
{
|
||
var hostPlayer = gameRun.GetHostPlayer();
|
||
if (hostPlayer != null)
|
||
{
|
||
playerProf = hostPlayer.GetProfession();
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
int col = white;
|
||
if (playerProf >= 0)
|
||
{
|
||
// In C++: col = (iProfReq & (1 << pHost->GetProfession())) ? ITEMDESC_COL_WHITE : ITEMDESC_COL_RED;
|
||
if ((profReq & (1 << playerProf)) == 0)
|
||
col = red;
|
||
}
|
||
|
||
string profFmt = GetItemDescString(DescriptipionMsg.ITEMDESC_PROFESSIONREQ);
|
||
AddDescText(col, false, profFmt);
|
||
|
||
// List all allowed professions
|
||
// In C++: for (int i=0; i < NUM_PROFESSION; i++) { if (iProfReq & (1 << i)) { m_strDesc += _AL(" "); AddDescText(col, false, pGameRun->GetProfName(i)); } }
|
||
// Profession message IDs matching C++ GetProfName: FIXMSG_PROF_WARRIOR, FIXMSG_PROF_MAGE, etc.
|
||
FixedMsg[] profMsgIds = new FixedMsg[]
|
||
{
|
||
FixedMsg.FIXMSG_PROF_WARRIOR, // 0
|
||
FixedMsg.FIXMSG_PROF_MAGE, // 1
|
||
FixedMsg.FIXMSG_PROF_MONK, // 2
|
||
FixedMsg.FIXMSG_PROF_HAG, // 3
|
||
FixedMsg.FIXMSG_PROF_ORC, // 4
|
||
FixedMsg.FIXMSG_PROF_GHOST, // 5
|
||
FixedMsg.FIXMSG_PROF_ARCHOR, // 6
|
||
FixedMsg.FIXMSG_PROF_ANGEL, // 7
|
||
FixedMsg.FIXMSG_PROF_JIANLING, // 8
|
||
FixedMsg.FIXMSG_PROF_MEILING, // 9
|
||
FixedMsg.FIXMSG_PROF_YEYING, // 10
|
||
FixedMsg.FIXMSG_PROF_YUEXIAN // 11
|
||
};
|
||
|
||
for (int i = 0; i < NUM_PROFESSION && i < profMsgIds.Length; i++)
|
||
{
|
||
if ((profReq & (1 << i)) != 0)
|
||
{
|
||
// In C++, space is always added before each profession name
|
||
m_strDesc += " ";
|
||
// Get profession name from fixed messages (like C++ GetProfName does)
|
||
// C++ uses: pStrTab->GetWideString(s_ProfDesc[i]) where s_ProfDesc[i] = FIXMSG_PROF_WARRIOR, etc.
|
||
string profName = null;
|
||
try
|
||
{
|
||
var fixedMsgs = EC_Game.GetFixedMsgs();
|
||
if (fixedMsgs != null && fixedMsgs.IsInitialized())
|
||
{
|
||
profName = fixedMsgs.GetWideString((int)profMsgIds[i]);
|
||
// Trim whitespace if present
|
||
if (!string.IsNullOrEmpty(profName))
|
||
profName = profName.Trim();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UnityEngine.Debug.LogWarning($"[EC_IvtrEquip] Failed to get profession name for index {i}: {ex.Message}");
|
||
}
|
||
|
||
// If we got a valid name, use it; otherwise use fallback
|
||
if (!string.IsNullOrEmpty(profName))
|
||
{
|
||
AddDescText(col, false, profName);
|
||
}
|
||
else
|
||
{
|
||
// Fallback: show profession index (should not happen if fixed_msg.txt is loaded correctly)
|
||
UnityEngine.Debug.LogWarning($"[EC_IvtrEquip] Profession name not found for index {i}, enum value {(int)profMsgIds[i]}, profReq={profReq}");
|
||
AddDescText(col, false, $"P{i}");
|
||
}
|
||
}
|
||
}
|
||
|
||
m_strDesc += "\\r";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add price description
|
||
/// </summary>
|
||
public void AddPriceDesc(int col, bool repair)
|
||
{
|
||
if (repair)
|
||
AddDescText(col, false, GetItemDescString(DescriptipionMsg.ITEMDESC_REPAIRCOST), GetRepairCost());
|
||
else
|
||
AddDescText(col, true, "Price: {0}", GetScaledPrice());
|
||
}
|
||
|
||
/// <summary>
|
||
/// Format property description
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Format refine data into a string
|
||
/// (hack function, do NOT use it in multi-thread environment)
|
||
/// </summary>
|
||
public string FormatRefineData(uint addonId)
|
||
{
|
||
DATA_TYPE dataType = DATA_TYPE.DT_INVALID;
|
||
elementdataman edm = EC_Game.GetElementDataMan();
|
||
if (edm == null)
|
||
return "";
|
||
|
||
object data = edm.get_data_ptr(addonId, ID_SPACE.ID_SPACE_ADDON, ref dataType);
|
||
if (dataType != DATA_TYPE.DT_EQUIPMENT_ADDON || data == null)
|
||
return "";
|
||
|
||
EQUIPMENT_ADDON pType = (EQUIPMENT_ADDON)data;
|
||
string szTxt = "";
|
||
|
||
// save current member for backup
|
||
string tmpDesc = m_strDesc;
|
||
m_strDesc = "";
|
||
|
||
// get the original refine data
|
||
int[] aRefines = new int[MAX_REFINEINDEX];
|
||
for (int i = 0; i < MAX_REFINEINDEX; i++)
|
||
aRefines[i] = 0;
|
||
|
||
int[] paramArray = new int[] { pType.param1, pType.param2, pType.param3 };
|
||
AddOneAddOnPropDesc((int)pType.id, paramArray, null, aRefines, true);
|
||
|
||
// get splitter
|
||
// In C++: g_pGame->GetGameRun()->GetUIManager()->GetInGameUIMan()->GetStringFromTable(8656)
|
||
string szSplitter = " "; // Default splitter
|
||
try
|
||
{
|
||
var gameRun = EC_Game.GetGameRun();
|
||
if (gameRun != null)
|
||
{
|
||
// Try to get from UI manager if available
|
||
// Note: This may not be fully implemented in C# yet
|
||
var fixedMsgs = EC_Game.GetFixedMsgs();
|
||
if (fixedMsgs != null)
|
||
{
|
||
string splitterStr = fixedMsgs.GetWideString(8656);
|
||
if (!string.IsNullOrEmpty(splitterStr))
|
||
szSplitter = splitterStr;
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Use default splitter if lookup fails
|
||
}
|
||
|
||
int findSplitter = 0;
|
||
for (int pos = 0; pos < szSplitter.Length; pos++)
|
||
{
|
||
findSplitter = m_strDesc.IndexOf(szSplitter[pos]);
|
||
if (findSplitter >= 0)
|
||
break;
|
||
}
|
||
|
||
szTxt = (findSplitter <= 0) ? m_strDesc : m_strDesc.Substring(0, findSplitter);
|
||
|
||
// restore member because previous method may modify it
|
||
m_strDesc = tmpDesc;
|
||
|
||
// replace all old splitter to new splitter
|
||
const string OldSplitter = "\\r";
|
||
const string NewSplitter = " ";
|
||
string result = "";
|
||
findSplitter = 0;
|
||
while (true)
|
||
{
|
||
int curfind = szTxt.IndexOf(OldSplitter, findSplitter);
|
||
if (curfind >= 0)
|
||
{
|
||
if (!string.IsNullOrEmpty(result))
|
||
result += NewSplitter;
|
||
if (curfind > findSplitter)
|
||
{
|
||
result += szTxt.Substring(findSplitter, curfind - findSplitter);
|
||
}
|
||
findSplitter = curfind + OldSplitter.Length;
|
||
if (findSplitter >= szTxt.Length)
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (!string.IsNullOrEmpty(result))
|
||
result += NewSplitter;
|
||
result += szTxt.Substring(findSplitter);
|
||
break;
|
||
}
|
||
}
|
||
szTxt = result;
|
||
|
||
// check the special refine property group
|
||
int[] ALLMAGIC_REFINE = new int[] { REFINE_GOLDDEF, REFINE_WOODDEF, REFINE_WATERDEF, REFINE_FIREDEF, REFINE_EARTHDEF };
|
||
int[] ALLDAMAGE_REFINE = new int[] { REFINE_PHYDAMAGE, REFINE_MAGICDAMAGE };
|
||
int[] ALLDEFENCE_REFINE = new int[] { REFINE_PHYDEF, REFINE_GOLDDEF, REFINE_WOODDEF, REFINE_WATERDEF, REFINE_FIREDEF, REFINE_EARTHDEF };
|
||
|
||
if (CheckSpecialRefineType(aRefines, ALLMAGIC_REFINE, ALLMAGIC_REFINE.Length))
|
||
{
|
||
int value = aRefines[ALLMAGIC_REFINE[0]];
|
||
// Format with %+d equivalent: always show sign
|
||
string szRefine = string.Format("{0} {1}{2}",
|
||
GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICDEF),
|
||
value >= 0 ? "+" : "", value);
|
||
if (!string.IsNullOrEmpty(szTxt))
|
||
szTxt += NewSplitter;
|
||
szTxt += szRefine;
|
||
}
|
||
else if (CheckSpecialRefineType(aRefines, ALLDAMAGE_REFINE, ALLDAMAGE_REFINE.Length))
|
||
{
|
||
int value = aRefines[ALLDAMAGE_REFINE[0]];
|
||
// Format with %+d equivalent: always show sign
|
||
string szRefine = string.Format("{0} {1}{2}",
|
||
GetItemDescString(DescriptipionMsg.ITEMDESC_ADDDAMAGE),
|
||
value >= 0 ? "+" : "", value);
|
||
if (!string.IsNullOrEmpty(szTxt))
|
||
szTxt += NewSplitter;
|
||
szTxt += szRefine;
|
||
}
|
||
else if (CheckSpecialRefineType(aRefines, ALLDEFENCE_REFINE, ALLDEFENCE_REFINE.Length))
|
||
{
|
||
int value = aRefines[ALLDEFENCE_REFINE[0]];
|
||
// Format with %+d equivalent: always show sign
|
||
string szRefine = string.Format("{0} {1}{2}",
|
||
GetItemDescString(DescriptipionMsg.ITEMDESC_DEFENCE),
|
||
value >= 0 ? "+" : "", value);
|
||
if (!string.IsNullOrEmpty(szTxt))
|
||
szTxt += NewSplitter;
|
||
szTxt += szRefine;
|
||
}
|
||
else
|
||
{
|
||
int descId = 0;
|
||
for (int refineIndex = 0; refineIndex < MAX_REFINEINDEX; refineIndex++)
|
||
{
|
||
if (aRefines[refineIndex] == 0)
|
||
continue;
|
||
|
||
// do NOT use loop because the enum value may changed
|
||
switch (refineIndex)
|
||
{
|
||
case REFINE_PHYDAMAGE:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE;
|
||
break;
|
||
case REFINE_MAGICDAMAGE:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_ADDMAGICDAMAGE;
|
||
break;
|
||
case REFINE_PHYDEF:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_PHYDEFENCE;
|
||
break;
|
||
case REFINE_GOLDDEF:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_GOLDDEFENCE;
|
||
break;
|
||
case REFINE_WOODDEF:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_WOODDEFENCE;
|
||
break;
|
||
case REFINE_WATERDEF:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_WATERDEFENCE;
|
||
break;
|
||
case REFINE_FIREDEF:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_FIREDEFENCE;
|
||
break;
|
||
case REFINE_EARTHDEF:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_EARTHDEFENCE;
|
||
break;
|
||
case REFINE_HP:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_ADDHP;
|
||
break;
|
||
case REFINE_DODGE:
|
||
descId = (int)DescriptipionMsg.ITEMDESC_DODGE;
|
||
break;
|
||
default:
|
||
descId = -1;
|
||
break;
|
||
}
|
||
|
||
if (descId >= 0)
|
||
{
|
||
int value = aRefines[refineIndex];
|
||
// Format with %+d equivalent: always show sign
|
||
string szRefine = string.Format("{0} {1}{2} ",
|
||
GetItemDescString((DescriptipionMsg)descId),
|
||
value >= 0 ? "+" : "", value);
|
||
if (!string.IsNullOrEmpty(szTxt))
|
||
szTxt += NewSplitter;
|
||
szTxt += szRefine;
|
||
}
|
||
}
|
||
}
|
||
|
||
return szTxt;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get refine addon ID (virtual method, override in derived classes)
|
||
/// </summary>
|
||
public virtual uint GetRefineAddOn()
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check the special refine property
|
||
/// </summary>
|
||
private bool CheckSpecialRefineType(int[] aRefines, int[] types, int typeCount)
|
||
{
|
||
// check the special refine property
|
||
int sameValue = 0;
|
||
|
||
for (int refineIndex = 0; refineIndex < MAX_REFINEINDEX; refineIndex++)
|
||
{
|
||
bool checked_ = false;
|
||
for (int i = 0; i < typeCount; i++)
|
||
{
|
||
if (types[i] == refineIndex)
|
||
{
|
||
if (sameValue == 0)
|
||
{
|
||
if (aRefines[refineIndex] == 0)
|
||
{
|
||
// specific property was not found
|
||
return false;
|
||
}
|
||
sameValue = aRefines[refineIndex];
|
||
}
|
||
else if (sameValue != aRefines[refineIndex])
|
||
{
|
||
// property values were different
|
||
return false;
|
||
}
|
||
|
||
checked_ = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// check other refine property
|
||
if (!checked_ && aRefines[refineIndex] != 0)
|
||
{
|
||
// has other refine property
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check property range value
|
||
/// </summary>
|
||
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
|
||
|
||
/// <summary>
|
||
/// Parse properties
|
||
/// </summary>
|
||
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++;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check if this equipment belongs to a suite
|
||
/// </summary>
|
||
public int GetSuiteID()
|
||
{
|
||
// This would normally query the game's suite equip table
|
||
Dictionary<int, int> suiteEquipTab = EC_Game.GetSuiteEquipTab();
|
||
if (suiteEquipTab.TryGetValue(GetTemplateID(), out int suiteId))
|
||
{
|
||
return suiteId;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get item description string by ID
|
||
/// Primary source: item_desc.txt table (loaded via EC_Game.GetItemDesc())
|
||
/// Fallback: Directly reads from item_desc.txt file when table lookup fails
|
||
/// </summary>
|
||
private string GetItemDescString(DescriptipionMsg id)
|
||
{
|
||
int enumValue = (int)id;
|
||
|
||
// First try to get from the loaded string table (item_desc.txt)
|
||
// The table contains strings indexed 0-416, matching enum values
|
||
var tab = EC_Game.GetItemDesc();
|
||
if (tab != null)
|
||
{
|
||
string s = tab.GetWideString(enumValue);
|
||
if (!string.IsNullOrEmpty(s)) return s;
|
||
}
|
||
|
||
// Fallback: Directly read f rom item_desc.txt file
|
||
// This handles cases where table lookup fails but the string exists in the file
|
||
try
|
||
{
|
||
string filePath = Path.Combine(Application.streamingAssetsPath, "configs", "item_desc.txt");
|
||
if (File.Exists(filePath))
|
||
{
|
||
string content = File.ReadAllText(filePath, Encoding.UTF8);
|
||
string[] lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
|
||
|
||
bool foundBegin = false;
|
||
int currentIndex = 0;
|
||
|
||
foreach (string line in lines)
|
||
{
|
||
string trimmed = line.Trim();
|
||
if (trimmed.Length == 0) continue;
|
||
|
||
if (trimmed.Equals("#_begin", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
foundBegin = true;
|
||
continue;
|
||
}
|
||
|
||
if (!foundBegin) continue;
|
||
|
||
// Skip comments
|
||
if (trimmed.StartsWith("#") || trimmed.StartsWith("//")) continue;
|
||
|
||
// Remove quotes if present
|
||
if (trimmed.StartsWith("\"") && trimmed.EndsWith("\""))
|
||
{
|
||
trimmed = trimmed.Substring(1, trimmed.Length - 2);
|
||
}
|
||
|
||
if (currentIndex == enumValue)
|
||
{
|
||
return trimmed;
|
||
}
|
||
|
||
currentIndex++;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogWarning($"[EC_IvtrEquip] Failed to read item_desc.txt fallback for ID {enumValue}: {ex.Message}");
|
||
}
|
||
|
||
// Final fallback: Return enum value as string
|
||
return enumValue.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add resistance property description with proper formatting
|
||
/// </summary>
|
||
private void AddResistDesc(int color, DescriptipionMsg resistId, int value, bool local)
|
||
{
|
||
string resistFmt = GetItemDescString(resistId);
|
||
int displayValue = local ? VisualizeFloatPercent(value) : value;
|
||
// Check if format string has placeholder, if not append value with + sign
|
||
if (resistFmt.Contains("%d") || resistFmt.Contains("%+d") || resistFmt.Contains("{0}"))
|
||
AddDescText(color, true, resistFmt, displayValue);
|
||
else
|
||
AddDescText(color, true, "{0} +{1}", resistFmt, displayValue);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add description text
|
||
/// </summary>
|
||
protected override void AddDescText(int color, bool newLine, string format, params object[] args)
|
||
{
|
||
// Add color prefix if color is specified
|
||
if (color >= 0)
|
||
{
|
||
string colorStr = GetColorString((DescriptipionMsg)color);
|
||
m_strDesc += colorStr;
|
||
}
|
||
|
||
// Call base implementation for format conversion (handles both {0} and %d styles)
|
||
base.AddDescText(-1, false, format, args);
|
||
|
||
// Add newline if requested (base class uses "\n", but equip uses "\\r")
|
||
if (newLine)
|
||
m_strDesc += "\\r";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get color string for color ID
|
||
/// Returns color codes in ^RRGGBB format (6 hex digits) for text formatting
|
||
/// </summary>
|
||
private string GetColorString(DescriptipionMsg colorId)
|
||
{
|
||
switch (colorId)
|
||
{
|
||
case DescriptipionMsg.ITEMDESC_COL_WHITE: return "^FFFFFF"; // White
|
||
case DescriptipionMsg.ITEMDESC_COL_GREEN: return "^00FF00"; // Green
|
||
case DescriptipionMsg.ITEMDESC_COL_YELLOW: return "^FFFF00"; // Yellow
|
||
case DescriptipionMsg.ITEMDESC_COL_DARKGOLD: return "^FF8C00"; // Dark Gold / Orange
|
||
case DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE: return "^5998FF"; // Light Blue
|
||
case DescriptipionMsg.ITEMDESC_COL_CYANINE: return "^00FFFF"; // Cyan
|
||
case DescriptipionMsg.ITEMDESC_COL_RED: return "^FF0000"; // Red
|
||
case DescriptipionMsg.ITEMDESC_COL_GRAY: return "^808080"; // Gray
|
||
default: return "^FFFFFF"; // Default to white
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Convert word color to Unity Color
|
||
/// </summary>
|
||
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
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check if property is sharpener property
|
||
/// </summary>
|
||
private bool IsSharpenerProperty(byte propType)
|
||
{
|
||
return propType >= 100 && propType <= 115;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Visualize float percent
|
||
/// </summary>
|
||
private int VisualizeFloatPercent(int value)
|
||
{
|
||
return (int)(IntToFloat(value) * 100.0f + 0.5f);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Helper method to convert int to float (bit reinterpretation)
|
||
/// </summary>
|
||
private float IntToFloat(int value)
|
||
{
|
||
return BitConverter.ToSingle(BitConverter.GetBytes(value), 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Helper method to convert float to int (bit reinterpretation)
|
||
/// </summary>
|
||
private int FloatToInt(float value)
|
||
{
|
||
return BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for normal integer values (replaces ADD_RANGE_VALUE_DESC_ID_NORMAL macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescIdNormal(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString), p0);
|
||
AddDescText(color, true, "~{0}", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), p0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for float values (replaces ADD_RANGE_VALUE_DESC_ID_FLOAT macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescIdFloat(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
float f0 = IntToFloat(p0);
|
||
float f1 = IntToFloat(p1);
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString), f0);
|
||
AddDescText(color, true, "~{0:F2}", f1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), f0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), f0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for percent values (replaces ADD_RANGE_VALUE_DESC_ID_PERCENT macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescIdPercent(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
if (local)
|
||
{
|
||
int v0 = VisualizeFloatPercent(p0);
|
||
int v1 = VisualizeFloatPercent(p1);
|
||
if (v0 != v1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString), v0);
|
||
AddDescText(color, true, "~{0}%%", v1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), v0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), p0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for minus percent values variant 1 (replaces ADD_RANGE_VALUE_DESC_ID_MINUS_PERCENT_1 macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescIdMinusPercent1(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
if (local)
|
||
{
|
||
int v0 = -VisualizeFloatPercent(p0);
|
||
int v1 = VisualizeFloatPercent(p1);
|
||
if (v0 != v1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString), v0);
|
||
AddDescText(color, true, "~{0}%%", v1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), v0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), -p0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for minus percent values variant 2 (replaces ADD_RANGE_VALUE_DESC_ID_MINUS_PERCENT_2 macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescIdMinusPercent2(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
if (local)
|
||
{
|
||
int v0 = -VisualizeFloatPercent(p0);
|
||
int v1 = VisualizeFloatPercent(p1);
|
||
if (v0 != v1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString), v0);
|
||
AddDescText(color, true, "~{0}%%", v1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), v0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), -VisualizeFloatPercent(p0));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for half values (replaces ADD_RANGE_VALUE_DESC_ID_HALF macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescIdHalf(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
int h0 = p0 / 2;
|
||
int h1 = p1 / 2;
|
||
if (local)
|
||
{
|
||
if (h0 != h1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString), h0);
|
||
AddDescText(color, true, "~{0}", h1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), h0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(idString), h0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add range value description for string normal values (replaces ADD_RANGE_VALUE_DESC_STR_NORMAL macro)
|
||
/// </summary>
|
||
private void AddRangeValueDescStrNormal(DescriptipionMsg idString, int p0, int p1, bool local, int color)
|
||
{
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString));
|
||
AddDescText(color, true, " %+d~%d", p0, p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, false, GetItemDescString(idString));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add one add-on property description
|
||
/// </summary>
|
||
private void AddOneAddOnPropDesc(int idProp, int[] param, int[] aPEEVals, int[] aRefines, bool local)
|
||
{
|
||
// Extract parameters from array
|
||
int p0 = param != null && param.Length > 0 ? param[0] : 0;
|
||
int p1 = param != null && param.Length > 1 ? param[1] : 0;
|
||
int p2 = param != null && param.Length > 2 ? param[2] : 0;
|
||
byte propType = GetPropertyType(idProp);
|
||
int color = -1;
|
||
|
||
if (!IsSharpenerProperty(propType))
|
||
{
|
||
switch (propType)
|
||
{
|
||
case 0: // ������
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_PHYDAMAGE] += p0;
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 1: // ����������
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXPHYDAMAGE), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXPHYDAMAGE), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_MAX_PHYDAMAGE] += p0;
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXPHYDAMAGE), p0);
|
||
}
|
||
break;
|
||
|
||
case 2: // ������(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDMGEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDMGEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 3: // ħ������
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_MAGICDAMAGE] += p0;
|
||
}
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMAGICDAMAGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 4: // ħ����������
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXMAGICDAMAGE), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXMAGICDAMAGE), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_MAX_MAGICDAMAGE] += p0;
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXMAGICDAMAGE), p0);
|
||
}
|
||
break;
|
||
|
||
case 5: // ħ������(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MAGICDMGEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MAGICDMGEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 6: // +���-�﹥
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_PHYDEF] += p0;
|
||
aPEEVals[PEEI_PHYDAMAGE] -= p1;
|
||
}
|
||
}
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 7: // +�﹥-���
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_PHYDAMAGE] += p0;
|
||
aPEEVals[PEEI_PHYDEF] -= p1;
|
||
}
|
||
}
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 8: // +ħ��-ħ��
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_MAGICDAMAGE] += p0;
|
||
aPEEVals[PEEI_GOLDDEF] -= p1;
|
||
aPEEVals[PEEI_WOODDEF] -= p1;
|
||
aPEEVals[PEEI_WATERDEF] -= p1;
|
||
aPEEVals[PEEI_FIREDEF] -= p1;
|
||
aPEEVals[PEEI_EARTHDEF] -= p1;
|
||
}
|
||
}
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMAGICDAMAGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICDEF));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 9: // �����ٶ�
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKTIME), -IntToFloat(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKTIME), -p0 * 0.05f);
|
||
}
|
||
break;
|
||
|
||
case 10: // ��������
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDATKDIST), IntToFloat(p0));
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
float fDist = IntToFloat(aPEEVals[PEEI_ATKDIST]) + IntToFloat(p0);
|
||
aPEEVals[PEEI_ATKDIST] = FloatToInt(fDist);
|
||
}
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDATKDIST), IntToFloat(p0));
|
||
}
|
||
break;
|
||
|
||
case 11: // ����ʱ��
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_CASTTIME), -VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_CASTTIME), -p0);
|
||
}
|
||
break;
|
||
|
||
case 12: // �������
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_PHYDEF] += p0;
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 13: // �������(%)
|
||
if (local)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFEXTRA), VisualizeFloatPercent(p0));
|
||
else
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFEXTRA), p0);
|
||
break;
|
||
|
||
case 14: // �����
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_GOLDDEF] += p0;
|
||
aPEEVals[PEEI_WOODDEF] += p0;
|
||
aPEEVals[PEEI_WATERDEF] += p0;
|
||
aPEEVals[PEEI_FIREDEF] += p0;
|
||
aPEEVals[PEEI_EARTHDEF] += p0;
|
||
}
|
||
}
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICDEF));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 15: // ���
|
||
if (local)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFENCE));
|
||
if (p0 != p1)
|
||
AddDescText(color, true, " %d~%d", p0, p1);
|
||
else
|
||
AddDescText(color, true, " %d", p0);
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_GOLDDEF] += p0;
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
break;
|
||
|
||
case 16: // ���(%)
|
||
if (local)
|
||
{
|
||
if ((p0) != (p1))
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFEXTRA), (p0));
|
||
AddDescText(color, true, "~-%.2f%%", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFEXTRA), (p0));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFEXTRA), (p0));
|
||
}
|
||
break;
|
||
|
||
case 17: // ľ��
|
||
if (local)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFENCE));
|
||
|
||
if (p0 != p1)
|
||
AddDescText(color, true, " %d~%d", p0, p1);
|
||
else
|
||
AddDescText(color, true, " %d", p0);
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_WOODDEF] += p0;
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
break;
|
||
|
||
case 18: // ľ��(%)
|
||
if (local)
|
||
{
|
||
if ((p0) != (p1))
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFEXTRA), (p0));
|
||
AddDescText(color, true, "~-%.2f%%", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFEXTRA), (p0));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFEXTRA), (p0));
|
||
}
|
||
break;
|
||
|
||
case 19: // ˮ��
|
||
if (local)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFENCE));
|
||
|
||
if (p0 != p1)
|
||
AddDescText(color, true, " %d~%d", p0, p1);
|
||
else
|
||
AddDescText(color, true, " %d", p0);
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_WATERDEF] += p0;
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
break;
|
||
|
||
case 20: // ˮ��(%)
|
||
if (local)
|
||
{
|
||
if ((p0) != (p1))
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFEXTRA), (p0));
|
||
AddDescText(color, true, "~-%.2f%%", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFEXTRA), (p0));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFEXTRA), (p0));
|
||
}
|
||
break;
|
||
|
||
case 21: // ���
|
||
if (local)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFENCE));
|
||
|
||
if (p0 != p1)
|
||
AddDescText(color, true, " %d~%d", p0, p1);
|
||
else
|
||
AddDescText(color, true, " %d", p0);
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_FIREDEF] += p0;
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
break;
|
||
|
||
case 22: // ���(%)
|
||
if (local)
|
||
{
|
||
if ((p0) != (p1))
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFEXTRA), (p0));
|
||
AddDescText(color, true, "~-%.2f%%", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFEXTRA), (p0));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFEXTRA), (p0));
|
||
}
|
||
break;
|
||
|
||
case 23: // ����
|
||
if (local)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFENCE));
|
||
|
||
if (p0 != p1)
|
||
AddDescText(color, true, " %d~%d", p0, p1);
|
||
else
|
||
AddDescText(color, true, " %d", p0);
|
||
}
|
||
else
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_EARTHDEF] += p0;
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
}
|
||
break;
|
||
|
||
case 24: // ����(%)
|
||
if (local)
|
||
{
|
||
if ((p0) != (p1))
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFEXTRA), (p0));
|
||
AddDescText(color, true, "~-%.2f%%", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFEXTRA), (p0));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFEXTRA), (p0));
|
||
}
|
||
break;
|
||
|
||
case 25: // +���(%)-���(%)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFEXTRA), VisualizeFloatPercent(p0));
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFEXTRA), -VisualizeFloatPercent(p1));
|
||
break;
|
||
|
||
case 26: // +ľ��(%)-���(%)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFEXTRA), VisualizeFloatPercent(p0));
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFEXTRA), -VisualizeFloatPercent(p1));
|
||
break;
|
||
|
||
case 27: // +ˮ��(%)-����(%)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFEXTRA), VisualizeFloatPercent(p0));
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFEXTRA), -VisualizeFloatPercent(p1));
|
||
break;
|
||
|
||
case 28: // +���(%)-ˮ��(%)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFEXTRA), VisualizeFloatPercent(p0));
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFEXTRA), -VisualizeFloatPercent(p1));
|
||
break;
|
||
|
||
case 29: // +����(%)-ľ��(%)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFEXTRA), VisualizeFloatPercent(p0));
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFEXTRA), -VisualizeFloatPercent(p1));
|
||
break;
|
||
|
||
case 30: // +���-���
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_GOLDDEF] += p0;
|
||
aPEEVals[PEEI_FIREDEF] -= p1;
|
||
}
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFENCE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 31: // +ľ��-���
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_WOODDEF] += p0;
|
||
aPEEVals[PEEI_GOLDDEF] -= p1;
|
||
}
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFENCE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 32: // +ˮ��-����
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_WATERDEF] += p0;
|
||
aPEEVals[PEEI_EARTHDEF] -= p1;
|
||
}
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFENCE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 33: // +���-ˮ��
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_FIREDEF] += p0;
|
||
aPEEVals[PEEI_WATERDEF] -= p1;
|
||
}
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFENCE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 34: // +����-ľ��
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_EARTHDEF] += p0;
|
||
aPEEVals[PEEI_WOODDEF] -= p1;
|
||
}
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFENCE));
|
||
AddDescText(color, true, " %+d", -p1);
|
||
break;
|
||
|
||
case 35: // HP
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_HP] += p0;
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDHP));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 36: // MP
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_MP] += p0;
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMP));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 37: // HP(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_HPEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_HPEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 38: // MP(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MPEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MPEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 39: // HP�ָ��ٶ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_HPRECOVER), p0 / 2);
|
||
break;
|
||
|
||
case 40: // MP�ָ��ٶ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_MPRECOVER), p0 / 2);
|
||
break;
|
||
|
||
case 41: // ����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
}
|
||
break;
|
||
|
||
case 42: // ����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
}
|
||
break;
|
||
|
||
case 43: // ����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
}
|
||
break;
|
||
|
||
case 44: // ����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
}
|
||
break;
|
||
|
||
case 45: // ����һ����
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE), p0);
|
||
}
|
||
break;
|
||
|
||
case 46: // ����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATING), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATING), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATING), p0);
|
||
}
|
||
break;
|
||
|
||
case 47: // ����(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATINGEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATINGEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 48: // �ƶ��ٶ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_RUNSPEED), IntToFloat(p0));
|
||
break;
|
||
|
||
case 49: // �ƶ��ٶ�(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_RUNSPEEDEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_RUNSPEEDEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 50: // ����
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
aPEEVals[PEEI_DODGE] += p0;
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_DODGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 51: // ����(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DODGEEXTRA), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DODGEEXTRA), p0);
|
||
}
|
||
break;
|
||
|
||
case 52: // �;ö�
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ENDURANCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 53: // �;ö�(%)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ENDURANCEEXTRA), VisualizeFloatPercent(p0));
|
||
break;
|
||
|
||
case 54: // ��������
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYRESIST), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYRESIST), p0);
|
||
}
|
||
break;
|
||
|
||
case 55: // ���Ӽ���
|
||
{
|
||
// Get skill description - TODO: Implement skill description retrieval
|
||
string skillDesc = $"Skill {p0}"; // Placeholder - needs proper implementation
|
||
AddDescText(color, true, "{0}", skillDesc);
|
||
break;
|
||
}
|
||
|
||
case 56: // װ������
|
||
if (local)
|
||
{
|
||
if (VisualizeFloatPercent(p0) != VisualizeFloatPercent(p1))
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_REQEXTRA), -VisualizeFloatPercent(p0));
|
||
AddDescText(color, true, "~%d", VisualizeFloatPercent(p1));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_REQEXTRA), -VisualizeFloatPercent(p0));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_REQEXTRA), -VisualizeFloatPercent(p0));
|
||
}
|
||
break;
|
||
|
||
case 57: // δ֪����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_RANDOMPROP));
|
||
break;
|
||
|
||
case 58: // ����ֵ�ӳ�
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EXP), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EXP), p0);
|
||
}
|
||
break;
|
||
|
||
case 59: // �����ȼ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATK_DEGREE), p0);
|
||
break;
|
||
|
||
case 60: // �����ȼ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DEF_DEGREE), p0);
|
||
break;
|
||
|
||
case 61: // �������%��
|
||
if (local)
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_TOTAL_DEFENCE_ADD), VisualizeFloatPercent(p0));
|
||
else
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_TOTAL_DEFENCE_ADD), (p0));
|
||
break;
|
||
|
||
case 62: // ����֮��
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PROFVIEW));
|
||
break;
|
||
|
||
case 63: // ����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_SOULPOWER), p0);
|
||
break;
|
||
|
||
case 64: // ��ϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_GOLDRESIST, p0, local);
|
||
break;
|
||
|
||
case 65: // ľϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_WOODRESIST, p0, local);
|
||
break;
|
||
|
||
case 66: // ˮϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_WATERRESIST, p0, local);
|
||
break;
|
||
|
||
case 67: // ��ϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_FIRERESIST, p0, local);
|
||
break;
|
||
|
||
case 68: // ��ϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_EARTHRESIST, p0, local);
|
||
break;
|
||
|
||
case 69: // �����(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICRESIST), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICRESIST), p0);
|
||
}
|
||
break;
|
||
|
||
case 70: // �����ȼ���Χ������ֵ������ã�
|
||
|
||
// ADD_RANGE_VALUE_DESC_ID_NORMAL equivalent
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ATK_DEGREE), p0);
|
||
AddDescText(color, true, "~{0}", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATK_DEGREE), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATK_DEGREE), p0);
|
||
}
|
||
break;
|
||
|
||
case 71: // �����ȼ���Χ������ֵ������ã�
|
||
AddRangeValueDescIdNormal(DescriptipionMsg.ITEMDESC_DEF_DEGREE, p0, p1, local, color);
|
||
break;
|
||
|
||
case 72: // ����һ����(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdPercent(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE, p0, p1, local, color);
|
||
break;
|
||
|
||
case 73: // HP��Χ������ֵ������ã�
|
||
AddRangeValueDescStrNormal(DescriptipionMsg.ITEMDESC_ADDHP, p0, p1, local, color);
|
||
break;
|
||
|
||
case 74: // MP��Χ������ֵ������ã�
|
||
AddRangeValueDescStrNormal(DescriptipionMsg.ITEMDESC_ADDMP, p0, p1, local, color);
|
||
break;
|
||
|
||
case 75: // ����(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdPercent(DescriptipionMsg.ITEMDESC_ATKRATINGEXTRA, p0, p1, local, color);
|
||
break;
|
||
|
||
case 76: // ���������Χ������ֵ������ã�
|
||
AddRangeValueDescStrNormal(DescriptipionMsg.ITEMDESC_PHYDEFENCE, p0, p1, local, color);
|
||
break;
|
||
|
||
case 77: // ���з�����Χ������ֵ������ã�
|
||
AddRangeValueDescStrNormal(DescriptipionMsg.ITEMDESC_ALLMAGICDEF, p0, p1, local, color);
|
||
break;
|
||
|
||
case 78: // �������(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdPercent(DescriptipionMsg.ITEMDESC_PHYRESIST, p0, p1, local, color);
|
||
break;
|
||
|
||
case 79: // ���м���(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdPercent(DescriptipionMsg.ITEMDESC_ALLMAGICRESIST, p0, p1, local, color);
|
||
break;
|
||
|
||
case 80: // ����ʱ��(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdMinusPercent1(DescriptipionMsg.ITEMDESC_CASTTIME, p0, p1, local, color);
|
||
break;
|
||
|
||
case 81: // �������뷶Χ������ֵ������ã�
|
||
AddRangeValueDescIdFloat(DescriptipionMsg.ITEMDESC_ADDATKDIST, p0, p1, local, color);
|
||
break;
|
||
|
||
case 82: // MP�ָ��ٶȷ�Χ������ֵ������ã�
|
||
AddRangeValueDescIdHalf(DescriptipionMsg.ITEMDESC_MPRECOVER, p0, p1, local, color);
|
||
break;
|
||
|
||
case 83: // �������(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdPercent(DescriptipionMsg.ITEMDESC_PHYDEFEXTRA, p0, p1, local, color);
|
||
break;
|
||
|
||
case 84: // ���з���(%)��Χ������ֵ������ã�
|
||
AddRangeValueDescIdPercent(DescriptipionMsg.ITEMDESC_TOTAL_DEFENCE_ADD, p0, p1, local, color);
|
||
break;
|
||
|
||
case 85: // HP�ָ��ٶȷ�Χ������ֵ������ã�
|
||
AddRangeValueDescIdHalf(DescriptipionMsg.ITEMDESC_HPRECOVER, p0, p1, local, color);
|
||
break;
|
||
|
||
case 86: // ������Χ������ֵ������ã�
|
||
AddRangeValueDescStrNormal(DescriptipionMsg.ITEMDESC_DODGE, p0, p1, local, color);
|
||
break;
|
||
|
||
case 87: // ����������Χ������ֵ������ã�
|
||
AddRangeValueDescIdNormal(DescriptipionMsg.ITEMDESC_MAXPHYDAMAGE, p0, p1, local, color);
|
||
break;
|
||
|
||
case 88: // ħ����������Χ������ֵ������ã�
|
||
AddRangeValueDescIdNormal(DescriptipionMsg.ITEMDESC_MAXMAGICDAMAGE, p0, p1, local, color);
|
||
break;
|
||
|
||
case 89: // װ������Χ������ֵ������ã�
|
||
AddRangeValueDescIdMinusPercent2(DescriptipionMsg.ITEMDESC_REQEXTRA, p0, p1, local, color);
|
||
break;
|
||
|
||
case 90: // ��ħ�ȼ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PENETRATION), p0);
|
||
break;
|
||
|
||
case 91: // ��ħ�ȼ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_RESILIENCE), p0);
|
||
break;
|
||
|
||
case 92: // +���+ħ��
|
||
if (!local)
|
||
{
|
||
if (aPEEVals != null)
|
||
{
|
||
aPEEVals[PEEI_PHYDEF] += p0;
|
||
aPEEVals[PEEI_GOLDDEF] += p1;
|
||
aPEEVals[PEEI_WOODDEF] += p1;
|
||
aPEEVals[PEEI_WATERDEF] += p1;
|
||
aPEEVals[PEEI_FIREDEF] += p1;
|
||
aPEEVals[PEEI_EARTHDEF] += p1;
|
||
}
|
||
}
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICDEF));
|
||
AddDescText(color, true, " %+d", p1);
|
||
break;
|
||
|
||
case 93: // ��ɫHP
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDHP));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 94: // ��ɫHP��Χ
|
||
AddRangeValueDescStrNormal(DescriptipionMsg.ITEMDESC_ADDHP, p0, p1, local, color);
|
||
break;
|
||
|
||
case 95: // ����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
break;
|
||
|
||
case 96: // ����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
break;
|
||
|
||
case 97: // ����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
break;
|
||
|
||
case 98: // ����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
break;
|
||
|
||
case 99: // ��ɫMP
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMP));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 160: // �����̶�ֵ
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_VIGOUR));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 200: // ����������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_PHYDAMAGE] += p0;
|
||
break;
|
||
|
||
case 201: // ����ħ������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_MAGICDAMAGE] += p0;
|
||
break;
|
||
|
||
case 202: // �����������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_PHYDEF] += p0;
|
||
break;
|
||
|
||
case 203: // �������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_GOLDDEF] += p0;
|
||
break;
|
||
|
||
case 204: // ����ľ��
|
||
if (aRefines != null)
|
||
aRefines[REFINE_WOODDEF] += p0;
|
||
break;
|
||
|
||
case 205: // ����ˮ��
|
||
if (aRefines != null)
|
||
aRefines[REFINE_WATERDEF] += p0;
|
||
break;
|
||
|
||
case 206: // �������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_FIREDEF] += p0;
|
||
break;
|
||
|
||
case 207: // ��������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_EARTHDEF] += p0;
|
||
break;
|
||
|
||
case 208: // ����HP
|
||
if (aRefines != null)
|
||
aRefines[REFINE_HP] += p0;
|
||
break;
|
||
|
||
case 209: // ��������
|
||
if (aRefines != null)
|
||
aRefines[REFINE_DODGE] += p0;
|
||
break;
|
||
|
||
case 210: // ���������
|
||
if (aRefines != null)
|
||
{
|
||
aRefines[REFINE_GOLDDEF] += p0;
|
||
aRefines[REFINE_WOODDEF] += p0;
|
||
aRefines[REFINE_WATERDEF] += p0;
|
||
aRefines[REFINE_FIREDEF] += p0;
|
||
aRefines[REFINE_EARTHDEF] += p0;
|
||
}
|
||
break;
|
||
|
||
case 211: // ���������� & ħ������
|
||
if (aRefines != null)
|
||
{
|
||
aRefines[REFINE_PHYDAMAGE] += p0;
|
||
aRefines[REFINE_MAGICDAMAGE] += p0;
|
||
}
|
||
break;
|
||
|
||
case 212: // ����������� & ħ������
|
||
if (aRefines != null)
|
||
{
|
||
aRefines[REFINE_PHYDEF] += p0;
|
||
aRefines[REFINE_GOLDDEF] += p0;
|
||
aRefines[REFINE_WOODDEF] += p0;
|
||
aRefines[REFINE_WATERDEF] += p0;
|
||
aRefines[REFINE_FIREDEF] += p0;
|
||
aRefines[REFINE_EARTHDEF] += p0;
|
||
}
|
||
|
||
break;
|
||
|
||
// �Կ��������
|
||
case 120: // �Կ̽��
|
||
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_GOLDDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 121: // �Կ�ľ��
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WOODDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 122: // �Կ�ˮ��
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_WATERDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 123: // �Կ̻��
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_FIREDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 124: // �Կ�����
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_EARTHDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 125: // �Կ�����
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATING), p0);
|
||
break;
|
||
|
||
case 126: // �Կ̶���
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_DODGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 127: // �Կ�MP
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMP));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 128: // �Կ�����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_VITALITY), p0);
|
||
}
|
||
break;
|
||
|
||
case 129: // �Կ�����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
}
|
||
break;
|
||
|
||
case 130: // �Կ�����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
}
|
||
break;
|
||
|
||
case 131: // �Կ�����
|
||
if (local)
|
||
{
|
||
if (p0 != p1)
|
||
{
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
AddDescText(color, true, "~%d", p1);
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
}
|
||
break;
|
||
|
||
case 132: // �Կ�HP
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDHP));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 133: // �Կ��������
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 134: // �Կ����з���
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICDEF));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 135: // �Կ�������
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 136: // �Կ�ħ������
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMAGICDAMAGE));
|
||
AddDescText(color, true, " %+d", p0);
|
||
break;
|
||
|
||
case 137: // �Կ̽�ϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_GOLDRESIST, p0, local);
|
||
break;
|
||
|
||
case 138: // �Կ�ľϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_WOODRESIST, p0, local);
|
||
break;
|
||
|
||
case 139: // �Կ�ˮϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_WATERRESIST, p0, local);
|
||
break;
|
||
|
||
case 140: // �Կ̻�ϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_FIRERESIST, p0, local);
|
||
break;
|
||
|
||
case 141: // �Կ���ϵ����(%)
|
||
AddResistDesc(color, DescriptipionMsg.ITEMDESC_EARTHRESIST, p0, local);
|
||
break;
|
||
|
||
case 142: // �Կ����м���(%)
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICRESIST), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICRESIST), p0);
|
||
}
|
||
break;
|
||
|
||
case 143: // �Կ�����һ����
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE), p0);
|
||
}
|
||
break;
|
||
|
||
case 144: // �Կ̹����ȼ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ATK_DEGREE), p0);
|
||
break;
|
||
|
||
case 145: // �Կ̷����ȼ�
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_DEF_DEGREE), p0);
|
||
break;
|
||
|
||
case 146: // �Կ���������
|
||
if (local)
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYRESIST), VisualizeFloatPercent(p0));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYRESIST), p0);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
AddDescText(color, true, GetItemDescString(DescriptipionMsg.ITEMDESC_ERRORPROP), idProp);
|
||
break;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Build add-ons properties description
|
||
/// </summary>
|
||
protected void BuildAddOnPropDesc(int[] aPEEVals, int[] aRefines)
|
||
{
|
||
if (Props.Count == 0)
|
||
return;
|
||
|
||
// Change color
|
||
m_strDesc += GetColorString(DescriptipionMsg.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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get soul power added by this equipment
|
||
/// </summary>
|
||
public int GetSoulPowerAdded()
|
||
{
|
||
int added = 0;
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Build tessera description (socketed gems/stones)
|
||
/// </summary>
|
||
protected void BuildTesseraDesc()
|
||
{
|
||
if (Holes.Count == 0)
|
||
return;
|
||
|
||
int cyanine = (int)DescriptipionMsg.ITEMDESC_COL_CYANINE;
|
||
|
||
for (int i = 0; i < Holes.Count; i++)
|
||
{
|
||
if (Holes[i] == 0)
|
||
continue;
|
||
|
||
// Get item name - would normally use CECIvtrItem::CreateItem
|
||
string itemName = EC_IvtrItemUtils.Instance.ResolveItemName(Holes[i]);
|
||
string descText = "null";
|
||
|
||
// Check if it's a stone and get its description
|
||
// In C++, this checks if pItem->GetClassID() == ICID_STONE
|
||
// and then gets weapon_desc or armor_desc based on equipment type
|
||
// For now, use item name as fallback
|
||
descText = itemName;
|
||
|
||
AddDescText(cyanine, true, GetItemDescString(DescriptipionMsg.ITEMDESC_2STRINGS), itemName, descText);
|
||
}
|
||
}
|
||
|
||
struct SUITEITEM
|
||
{
|
||
public bool bEnabled;
|
||
public int tid;
|
||
public char[] szName;
|
||
public string Name => new string(szName);
|
||
public SUITEITEM(bool bEnabled, int tid)
|
||
{
|
||
this.bEnabled = bEnabled;
|
||
this.tid = tid;
|
||
this.szName = new char[32];
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// Add suite description
|
||
/// </summary>
|
||
protected void AddSuiteDesc()
|
||
{
|
||
int idSuite = GetSuiteID();
|
||
if (idSuite == 0)
|
||
return; // This equipment isn't one of any suite
|
||
|
||
// Get suite info
|
||
DATA_TYPE dataType = DATA_TYPE.DT_INVALID;
|
||
elementdataman dataMan = EC_Game.GetElementDataMan();
|
||
object pData = dataMan.get_data_ptr((uint)idSuite, ID_SPACE.ID_SPACE_ESSENCE, ref dataType);
|
||
if (dataType != DATA_TYPE.DT_SUITE_ESSENCE)
|
||
{
|
||
// ASSERT in C++
|
||
return;
|
||
}
|
||
SUITE_ESSENCE pSuiteEss = (SUITE_ESSENCE)pData;
|
||
CECHostPlayer hostPlayer = EC_Game.GetGameRun().GetHostPlayer();
|
||
|
||
// Colors
|
||
int iNameCol = DecideNameCol();
|
||
int lblue = (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE;
|
||
int green = (int)DescriptipionMsg.ITEMDESC_COL_GREEN;
|
||
int gray = (int)DescriptipionMsg.ITEMDESC_COL_GRAY;
|
||
int white = (int)DescriptipionMsg.ITEMDESC_COL_WHITE;
|
||
int yellow = (int)DescriptipionMsg.ITEMDESC_COL_YELLOW;
|
||
|
||
// Save current description
|
||
string strCurDesc = m_strDesc;
|
||
bool bShowDetail = true;
|
||
if (hostPlayer.GetEquipment() != m_pDescIvtr)
|
||
bShowDetail = false;
|
||
else
|
||
{
|
||
for(int i = 0; i < m_pDescIvtr.GetSize(); i++)
|
||
{
|
||
EC_IvtrItem pItem = m_pDescIvtr.GetItem(i);
|
||
if (pItem == null)
|
||
{
|
||
bShowDetail = false;
|
||
continue;
|
||
}
|
||
if (pItem.m_tid == this.m_tid)
|
||
{
|
||
bShowDetail = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!bShowDetail)
|
||
{
|
||
m_strDesc = "";
|
||
AddDescText(iNameCol, false, "{0} {1}/{2}", pSuiteEss.Name, 0, pSuiteEss.max_equips);
|
||
m_strDesc = strCurDesc + m_strDesc;
|
||
return;
|
||
}
|
||
// Maximum number of suite items
|
||
const int MAX_NUM = 12;
|
||
SUITEITEM[] aSuiteItems = new SUITEITEM[MAX_NUM];
|
||
|
||
|
||
int maxEquips = (pSuiteEss.max_equips > MAX_NUM) ? MAX_NUM : (int)pSuiteEss.max_equips;
|
||
for(int i = 0; i < maxEquips; i++)
|
||
{
|
||
aSuiteItems[i].bEnabled = false;
|
||
aSuiteItems[i].tid = (int)pSuiteEss.equipments[i].id;
|
||
aSuiteItems[i].szName = new char[32];
|
||
aSuiteItems[i].szName[0] = '\0';
|
||
EC_IvtrItem pEquipItem = CreateItem((int)pSuiteEss.equipments[i].id, 0,1);
|
||
if (pEquipItem != null)
|
||
{
|
||
aSuiteItems[i].szName = pEquipItem.GetName().ToCharArray();
|
||
//delete pEquipItem;
|
||
}
|
||
else
|
||
{
|
||
aSuiteItems[i].tid = 0;
|
||
}
|
||
}
|
||
int iItemCnt;
|
||
int[] aEquipped = new int[MAX_NUM];
|
||
iItemCnt = hostPlayer.GetEquippedSuiteItem(idSuite,ref aEquipped);
|
||
if(iItemCnt == 0) return;
|
||
|
||
//m_strDesc += "\\r\\r";
|
||
// Build suite addon properties at first
|
||
for (int i = 0; i < MAX_NUM; i++)
|
||
{
|
||
for(int j = 0; j < iItemCnt; j++)
|
||
{
|
||
if (aSuiteItems[i].tid == aEquipped[j])
|
||
{
|
||
aSuiteItems[i].bEnabled = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(iItemCnt > 1)
|
||
{
|
||
// Change color
|
||
AddDescText(lblue, false, "");
|
||
|
||
for (int i=1; i < iItemCnt; i++)
|
||
{
|
||
int idProp = (int)pSuiteEss.addons[i-1].id;
|
||
if (idProp == 0)
|
||
continue;
|
||
|
||
pData = dataMan.get_data_ptr((uint)idProp, ID_SPACE.ID_SPACE_ADDON, ref dataType);
|
||
if (dataType != DATA_TYPE.DT_EQUIPMENT_ADDON)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
EQUIPMENT_ADDON pAddOn = (EQUIPMENT_ADDON)pData;
|
||
|
||
AddDescText(-1, false, "(%d) ", i+1);
|
||
AddDescText(-1, true, "%s", pAddOn.Name);
|
||
}
|
||
}
|
||
// Add suite name
|
||
AddDescText(yellow/*iNameCol*/, true, "{0} ({1} / {2})", pSuiteEss.Name, iItemCnt, pSuiteEss.max_equips);
|
||
for (int i=0; i < pSuiteEss.max_equips; i++)
|
||
{
|
||
SUITEITEM suiteItem = aSuiteItems[i];
|
||
if (suiteItem.tid == 0)
|
||
continue;
|
||
|
||
int col = suiteItem.bEnabled ? green : gray;
|
||
bool bRet = (i == pSuiteEss.max_equips-1) ? false : true;
|
||
|
||
// Add item name
|
||
AddDescText(col, bRet, " %s", suiteItem.Name);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add destroying description
|
||
/// </summary>
|
||
public void AddDestroyingDesc(int dropItemID, int num)
|
||
{
|
||
if (!IsDestroying() || dropItemID == 0 || num == 0)
|
||
return;
|
||
|
||
// Get destroying info
|
||
DATA_TYPE dataType = DATA_TYPE.DT_INVALID;
|
||
elementdataman dataMan = EC_Game.GetElementDataMan();
|
||
object pData = dataMan.get_data_ptr((uint)dropItemID, ID_SPACE.ID_SPACE_ESSENCE, ref dataType);
|
||
|
||
if (dataType != DATA_TYPE.DT_ELEMENT_ESSENCE)
|
||
{
|
||
// ASSERT(0) in C++
|
||
return;
|
||
}
|
||
|
||
// Cast to ELEMENT_ESSENCE to get name
|
||
// In C#, we'd need to access the name field from the essence data
|
||
string essenceName = "Repair Item"; // TODO: Get from ELEMENT_ESSENCE.name
|
||
|
||
int red = (int)DescriptipionMsg.ITEMDESC_COL_RED;
|
||
|
||
m_strDesc += "\\r";
|
||
AddDescText(red, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EQUIP_DESTROYING));
|
||
|
||
AddDescText(red, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EQUIP_REPAIR_NEED_ITEM), essenceName);
|
||
AddDescText(red, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EQUIP_REPAIR_NEED_ITEMCNT), (int)Math.Ceiling(num * 1.2));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add reputation requirement description
|
||
/// </summary>
|
||
protected void AddReputationReqDesc()
|
||
{
|
||
if (ReputationReq == 0)
|
||
return;
|
||
|
||
// Get host player reputation
|
||
// In C++: CECHostPlayer* pHost = g_pGame->GetGameRun()->GetHostPlayer();
|
||
// int col = pHost->GetReputation() >= m_iReputationReq ? ITEMDESC_COL_WHITE : ITEMDESC_COL_RED;
|
||
int playerReputation = 0; // TODO: Get from host player
|
||
int col = playerReputation >= ReputationReq ?
|
||
(int)DescriptipionMsg.ITEMDESC_COL_WHITE :
|
||
(int)DescriptipionMsg.ITEMDESC_COL_RED;
|
||
|
||
AddDescText(col, true, GetItemDescString(DescriptipionMsg.ITEMDESC_REPUTATION_REQ), ReputationReq);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get engraved property number
|
||
/// </summary>
|
||
public int GetEngravedPropertyNum()
|
||
{
|
||
int num = 0;
|
||
foreach (Property prop in Props)
|
||
{
|
||
if (prop.Engraved)
|
||
num++;
|
||
}
|
||
return num;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add sharpener description (磨刀石 properties)
|
||
/// </summary>
|
||
protected void AddSharpenerDesc()
|
||
{
|
||
if (Props.Count == 0)
|
||
return;
|
||
|
||
int color = (int)DescriptipionMsg.ITEMDESC_COL_LIGHTBLUE;
|
||
|
||
// Check if all sharpener properties have the same expire time
|
||
bool sameExpireTime = true;
|
||
bool findFirst = true;
|
||
int lastExpireTime = 0;
|
||
|
||
foreach (Property prop in Props)
|
||
{
|
||
if (prop.Embed || prop.Suite || prop.Engraved)
|
||
continue;
|
||
|
||
byte propType = GetPropertyType(prop.Type);
|
||
if (!IsSharpenerProperty(propType))
|
||
continue;
|
||
|
||
int p1 = prop.Params.Length > 1 ? prop.Params[1] : 0;
|
||
if (findFirst)
|
||
{
|
||
// Found first sharpener property
|
||
lastExpireTime = p1;
|
||
findFirst = false;
|
||
}
|
||
else
|
||
{
|
||
// Found another sharpener property
|
||
if (p1 != lastExpireTime)
|
||
{
|
||
// Expire times are different
|
||
sameExpireTime = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (findFirst)
|
||
{
|
||
// Didn't find any sharpener property
|
||
return;
|
||
}
|
||
|
||
bool firstProp = true;
|
||
foreach (Property prop in Props)
|
||
{
|
||
if (prop.Embed || prop.Suite || prop.Engraved)
|
||
continue;
|
||
|
||
byte propType = GetPropertyType(prop.Type);
|
||
if (!IsSharpenerProperty(propType))
|
||
continue;
|
||
|
||
if (firstProp)
|
||
{
|
||
m_strDesc += "\\r";
|
||
firstProp = false;
|
||
}
|
||
|
||
// New line
|
||
m_strDesc += "\\r";
|
||
|
||
int p0 = prop.Params.Length > 0 ? prop.Params[0] : 0; // First parameter value
|
||
int p1 = prop.Params.Length > 1 ? prop.Params[1] : 0; // Expire time
|
||
|
||
// Add property description
|
||
switch (propType)
|
||
{
|
||
case 100: // Sharpener physical damage
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE));
|
||
AddDescText(color, false, p0 >= 0 ? " +{0}" : " {0}", p0);
|
||
break;
|
||
|
||
case 101: // Sharpener max physical damage
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXPHYDAMAGE), p0);
|
||
break;
|
||
|
||
case 102: // Sharpener magic damage
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDMAGICDAMAGE));
|
||
AddDescText(color, false, p0 >= 0 ? " +{0}" : " {0}", p0);
|
||
break;
|
||
|
||
case 103: // Sharpener max magic damage
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_MAXMAGICDAMAGE), p0);
|
||
break;
|
||
|
||
case 104: // Sharpener physical defence
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_PHYDEFENCE));
|
||
AddDescText(color, false, p0 >= 0 ? " +{0}" : " {0}", p0);
|
||
break;
|
||
|
||
case 105: // Sharpener HP
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDHP));
|
||
AddDescText(color, false, p0 >= 0 ? " +{0}" : " {0}", p0);
|
||
break;
|
||
|
||
case 106: // Sharpener strength
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_STRENGTH), p0);
|
||
break;
|
||
|
||
case 107: // Sharpener agility
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_AGILITY), p0);
|
||
break;
|
||
|
||
case 108: // Sharpener energy
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ENERGY), p0);
|
||
break;
|
||
|
||
case 109: // Sharpener attack rating
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ATKRATING), p0);
|
||
break;
|
||
|
||
case 110: // Sharpener deadly strike
|
||
if (prop.Local)
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE), VisualizeFloatPercent(p0));
|
||
else
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_DEADLYSTRIKE), p0);
|
||
break;
|
||
|
||
case 111: // Sharpener attack degree
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ATK_DEGREE), p0);
|
||
break;
|
||
|
||
case 112: // Sharpener defence degree
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_DEF_DEGREE), p0);
|
||
break;
|
||
|
||
case 113: // Sharpener cast time
|
||
if (prop.Local)
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_CASTTIME), -VisualizeFloatPercent(p0));
|
||
else
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_CASTTIME), -p0);
|
||
break;
|
||
|
||
case 114: // Sharpener magic defence
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ALLMAGICDEF));
|
||
AddDescText(color, false, p0 >= 0 ? " +{0}" : " {0}", p0);
|
||
break;
|
||
|
||
case 115: // Sharpener ride pet speed
|
||
AddDescText(color, false, GetItemDescString(DescriptipionMsg.ITEMDESC_ADDRIDEONPETSPEED), IntToFloat(p0));
|
||
break;
|
||
|
||
default:
|
||
// ASSERT(false) in C++
|
||
continue;
|
||
}
|
||
|
||
// If expire times are different, add expire time after each property
|
||
if (!sameExpireTime)
|
||
{
|
||
if (p1 != 0)
|
||
{
|
||
m_strDesc += " ";
|
||
AddExpireTimeDesc(p1);
|
||
TrimLastReturn();
|
||
}
|
||
}
|
||
}
|
||
|
||
// If expire times are the same, add expire time at the end
|
||
if (sameExpireTime)
|
||
{
|
||
if (lastExpireTime != 0)
|
||
{
|
||
m_strDesc += "\\r";
|
||
AddExpireTimeDesc(lastExpireTime);
|
||
TrimLastReturn();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Append engraved property descriptions to the current description buffer.
|
||
/// Mirrors the behaviour of the original C++ AddEngravedDesc.
|
||
/// </summary>
|
||
protected void AddEngravedDesc()
|
||
{
|
||
if (Props.Count == 0)
|
||
return;
|
||
|
||
// Change color
|
||
bool firstProp = true;
|
||
|
||
foreach (Property prop in Props)
|
||
{
|
||
if (!prop.Engraved)
|
||
continue;
|
||
|
||
if (firstProp)
|
||
{
|
||
firstProp = false;
|
||
m_strDesc += "\\r";
|
||
m_strDesc += GetColorString(DescriptipionMsg.ITEMDESC_COL_YELLOW);
|
||
}
|
||
AddOneAddOnPropDesc(prop.Type, prop.Params, null, null, prop.Local);
|
||
}
|
||
|
||
if (!firstProp)
|
||
{
|
||
// Trim last return after engraved properties
|
||
TrimLastReturn();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Append maker description (signature / crafted by) to the description buffer.
|
||
/// </summary>
|
||
protected void AddMakerDesc()
|
||
{
|
||
if (string.IsNullOrEmpty(Maker))
|
||
return;
|
||
|
||
m_strDesc += "\\r";
|
||
// For signed marks (IMT_SIGN), Maker already contains color codes and formatted text.
|
||
if (MadeFrom == (byte)ITEM_MAKE_TAG.IMT_SIGN)
|
||
{
|
||
m_strDesc += Maker;
|
||
}
|
||
else
|
||
{
|
||
// Normal "made by" line using item-desc string if available
|
||
string fmt = GetItemDescString(DescriptipionMsg.ITEMDESC_MADEFROM);
|
||
if (string.IsNullOrEmpty(fmt))
|
||
{
|
||
fmt = "Made by {0}";
|
||
}
|
||
AddDescText((int)DescriptipionMsg.ITEMDESC_COL_GREEN, false, fmt, Maker);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Trim the last '\r' in description string
|
||
/// </summary>
|
||
private void TrimLastReturn()
|
||
{
|
||
int len = m_strDesc.Length;
|
||
if (len >= 2 && m_strDesc[len - 2] == '\\' && m_strDesc[len - 1] == 'r')
|
||
{
|
||
m_strDesc = m_strDesc.Substring(0, len - 2);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add expire time description
|
||
/// </summary>
|
||
private void AddExpireTimeDesc()
|
||
{
|
||
if (ExpireDate == 0)
|
||
return;
|
||
|
||
int green = (int)DescriptipionMsg.ITEMDESC_COL_GREEN;
|
||
int yellow = (int)DescriptipionMsg.ITEMDESC_COL_YELLOW;
|
||
int gold = (int)DescriptipionMsg.ITEMDESC_COL_DARKGOLD;
|
||
int red = (int)DescriptipionMsg.ITEMDESC_COL_RED;
|
||
|
||
// Get server GMT time (this would normally come from EC_Game)
|
||
long serverTime = 0; // TODO: Get from EC_Game.GetServerGMTTime()
|
||
long timeLeft = ExpireDate - serverTime;
|
||
if (timeLeft < 0) timeLeft = 0;
|
||
|
||
if (timeLeft > 24 * 3600)
|
||
{
|
||
AddDescText(green, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EXPIRETIME_DAY),
|
||
(int)(timeLeft / (24 * 3600)), (int)((timeLeft % (24 * 3600)) / 3600));
|
||
}
|
||
else if (timeLeft > 3600)
|
||
{
|
||
AddDescText(yellow, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EXPIRETIME_HOUR_MIN),
|
||
(int)(timeLeft / 3600), (int)((timeLeft % 3600) / 60));
|
||
}
|
||
else if (timeLeft > 60)
|
||
{
|
||
AddDescText(gold, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EXPIRETIME_MIN_SEC),
|
||
(int)(timeLeft / 60), (int)(timeLeft % 60));
|
||
}
|
||
else
|
||
{
|
||
AddDescText(red, true, GetItemDescString(DescriptipionMsg.ITEMDESC_EXPIRETIME_SECOND), (int)timeLeft);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Add expire time description with specific expire date
|
||
/// </summary>
|
||
private void AddExpireTimeDesc(int expireDate)
|
||
{
|
||
int temp = ExpireDate;
|
||
ExpireDate = expireDate;
|
||
AddExpireTimeDesc();
|
||
ExpireDate = temp;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get preview info
|
||
/// </summary>
|
||
public virtual string GetPreviewInfo()
|
||
{
|
||
m_strDesc = "";
|
||
BuildAddOnPropDesc(null, null);
|
||
return m_strDesc;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get add-on property description
|
||
/// </summary>
|
||
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(DescriptipionMsg.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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get engrave description
|
||
/// </summary>
|
||
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(DescriptipionMsg.ITEMDESC_COL_YELLOW);
|
||
|
||
foreach (Property prop in Props)
|
||
{
|
||
if (prop.Engraved)
|
||
AddOneAddOnPropDesc(prop.Type, prop.Params, aPEEVals, aRefines, prop.Local);
|
||
}
|
||
|
||
return m_strDesc;
|
||
}
|
||
public struct RefineEffect
|
||
{
|
||
int m_refineIndex;
|
||
public int RefineIndex{ get { return m_refineIndex; } set { m_refineIndex = value; } }
|
||
int m_incEffect;
|
||
public int IncEffect{ get { return m_incEffect; } set { m_incEffect = value; } }
|
||
|
||
int[] m_aPEEVals;
|
||
public int[] APEEVals{ get { return m_aPEEVals; } set { m_aPEEVals = value; } }
|
||
int[] m_aRefines;
|
||
public int[] ARefines{ get { return m_aRefines; } set { m_aRefines = value; } }
|
||
|
||
string m_clrAttribute;
|
||
public string ClrAttribute{ get { return m_clrAttribute; } set { m_clrAttribute = value; } }
|
||
string m_clrEffect;
|
||
public string ClrEffect{ get { return m_clrEffect; } set { m_clrEffect = value; } }
|
||
|
||
public RefineEffect(int[] aPEEVals, int[] aRefines, string clrAttribute, string clrEffect)
|
||
{
|
||
m_refineIndex = -1;
|
||
m_incEffect = 0;
|
||
m_aPEEVals = aPEEVals;
|
||
m_aRefines = aRefines;
|
||
m_clrAttribute = clrAttribute;
|
||
m_clrEffect = clrEffect;
|
||
}
|
||
|
||
public void Set(int refineIndex, int incEffect){
|
||
m_refineIndex = refineIndex;
|
||
m_incEffect = incEffect;
|
||
}
|
||
|
||
public int GetIncEffect(){
|
||
return m_incEffect;
|
||
}
|
||
public string GetClrAttribute(){
|
||
return m_clrAttribute;
|
||
}
|
||
public string GetClrEffect(){
|
||
return m_clrEffect;
|
||
}
|
||
};
|
||
public virtual bool GetRefineEffectFor(string strEffect, RefineEffect rhs){ return false; }
|
||
public static int CalcRefineEffect(int refineLevel, int baseEffect)
|
||
{
|
||
const int MAX_REFINE_LEVEL = 12;
|
||
float[] refine_factor = new float[MAX_REFINE_LEVEL + 1]
|
||
{ 0, 1.0f, 2.0f, 3.05f, 4.3f, 5.75f, 7.55f, 9.95f, 13f, 17.05f, 22.3f, 29f, 37.5f };
|
||
if (refineLevel >= 0 && refineLevel <= MAX_REFINE_LEVEL){
|
||
return (int)(baseEffect * refine_factor[refineLevel] + 0.1f);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
/// <summary>
|
||
/// Equipment addon description class
|
||
/// </summary>
|
||
public class EC_IvtrEquipAddonDesc
|
||
{
|
||
private object m_pAddon;
|
||
private string m_strDesc = "";
|
||
|
||
/// <summary>
|
||
/// Set addon
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|