done skill combo
This commit is contained in:
+24
-22
@@ -17,9 +17,9 @@ namespace PerfectWorld.Scripts.Managers
|
||||
/// <summary>
|
||||
/// Arrow item class (cac loai mui ten)
|
||||
/// </summary>
|
||||
public class EC_IvtrArrow : EC_IvtrEquip
|
||||
public class CECIvtrArrow : EC_IvtrEquip
|
||||
{
|
||||
protected IVTR_ESSENCE_ARROW m_Essence; // Arrow essence data
|
||||
protected IVTR_ESSENCE_ARROW m_Essence; // Arrow essence data
|
||||
|
||||
// Data in database
|
||||
protected PROJECTILE_TYPE m_pDBType;
|
||||
@@ -29,30 +29,30 @@ namespace PerfectWorld.Scripts.Managers
|
||||
/// </summary>
|
||||
/// <param name="tid">Template id</param>
|
||||
/// <param name="expire_date">Expire date</param>
|
||||
public EC_IvtrArrow(int tid, int expire_date) : base(tid, expire_date)
|
||||
public CECIvtrArrow(int tid, int expire_date) : base(tid, expire_date)
|
||||
{
|
||||
m_iCID = (int)InventoryClassId.ICID_ARROW;
|
||||
m_iCID = (int)InventoryClassId.ICID_ARROW;
|
||||
|
||||
m_Essence = new IVTR_ESSENCE_ARROW();
|
||||
|
||||
// Get database data
|
||||
elementdataman pDB = ElementDataManProvider.GetElementDataMan();
|
||||
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
|
||||
m_pDBEssence = (PROJECTILE_ESSENCE)pDB.get_data_ptr((uint)tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||||
m_pDBType = (PROJECTILE_TYPE)pDB.get_data_ptr((uint)m_pDBEssence.type, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||||
m_pDBEssence = (PROJECTILE_ESSENCE)pDB.get_data_ptr((uint)tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||||
m_pDBType = (PROJECTILE_TYPE)pDB.get_data_ptr((uint)m_pDBEssence.type, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||||
|
||||
m_iPileLimit = m_pDBEssence.pile_num_max;
|
||||
m_iPrice = m_pDBEssence.price;
|
||||
m_iShopPrice = m_pDBEssence.shop_price;
|
||||
m_iProcType = (int)m_pDBEssence.proc_type;
|
||||
m_i64EquipMask = EC_IvtrType.EQUIP_MASK64_PROJECTILE;
|
||||
m_iPileLimit = m_pDBEssence.pile_num_max;
|
||||
m_iPrice = m_pDBEssence.price;
|
||||
m_iShopPrice = m_pDBEssence.shop_price;
|
||||
m_iProcType = (int)m_pDBEssence.proc_type;
|
||||
m_i64EquipMask = EC_IvtrType.EQUIP_MASK64_PROJECTILE;
|
||||
}
|
||||
|
||||
public EC_IvtrArrow(EC_IvtrArrow other) : base(other)
|
||||
public CECIvtrArrow(CECIvtrArrow other) : base(other)
|
||||
{
|
||||
m_pDBType = other.m_pDBType;
|
||||
m_pDBEssence = other.m_pDBEssence;
|
||||
m_Essence = other.m_Essence;
|
||||
m_pDBType = other.m_pDBType;
|
||||
m_pDBEssence = other.m_pDBEssence;
|
||||
m_Essence = other.m_Essence;
|
||||
}
|
||||
|
||||
public override bool SetItemInfo(byte[] pInfoData, int iDataLen)
|
||||
@@ -67,11 +67,11 @@ namespace PerfectWorld.Scripts.Managers
|
||||
CECDataReader dr = new CECDataReader(pInfoData, iDataLen);
|
||||
|
||||
// Skip equip requirements and endurance
|
||||
dr.Offset(5 * sizeof (int), CECDataReader.SEEK_CUR);
|
||||
dr.Offset(5 * sizeof(int), CECDataReader.SEEK_CUR);
|
||||
|
||||
int iEssenceSize = dr.ReadInt();
|
||||
//ASSERT(iEssenceSize == sizeof (IVTR_ESSENCE_ARROW));
|
||||
|
||||
|
||||
m_Essence = new IVTR_ESSENCE_ARROW(dr.ReadData(iEssenceSize));
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -105,6 +105,8 @@ namespace PerfectWorld.Scripts.Managers
|
||||
}
|
||||
return base.GetName(); // Fallback to base class method
|
||||
}
|
||||
public IVTR_ESSENCE_ARROW GetEssence() { return m_Essence; }
|
||||
public PROJECTILE_TYPE GetDBSubType() { return m_pDBType; }
|
||||
|
||||
// Get item description text
|
||||
protected override string GetNormalDesc(bool bRepair)
|
||||
@@ -128,22 +130,22 @@ namespace PerfectWorld.Scripts.Managers
|
||||
AddDescText(namecol, true, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NAMENUMBER), GetName(), m_iCount);
|
||||
else
|
||||
AddDescText(namecol, true, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NAME), GetName());
|
||||
|
||||
|
||||
AddIDDescText();
|
||||
|
||||
AddExpireTimeDesc();
|
||||
|
||||
// Weapon requirement
|
||||
AddDescText(white, true, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_WEAPONREQ), m_Essence.iWeaponReqLow,
|
||||
AddDescText(white, true, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_WEAPONREQ), m_Essence.iWeaponReqLow,
|
||||
m_Essence.iWeaponReqHigh, m_pDBType.Name);
|
||||
|
||||
// Damage enhance
|
||||
if (m_pDBEssence.damage_enhance != 0)
|
||||
if (m_pDBEssence.damage_enhance != 0)
|
||||
{
|
||||
AddDescText(-1, false, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_ADDPHYDAMAGE));
|
||||
AddDescText(-1, true, " %+d", m_pDBEssence.damage_enhance);
|
||||
}
|
||||
|
||||
|
||||
// Add addon properties
|
||||
if (strAddon.Length > 0)
|
||||
m_strDesc += strAddon;
|
||||
@@ -153,7 +155,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
|
||||
// Suite description
|
||||
AddSuiteDesc();
|
||||
|
||||
|
||||
// Extend description
|
||||
AddExtDescText();
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f479e02d3a184f94bb046bc6cbf64ba1
|
||||
+43
-3
@@ -67,7 +67,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
/// <summary>
|
||||
/// Weapon item class (cac loai vu khi)
|
||||
/// </summary>
|
||||
public class EC_IvtrWeapon : EC_IvtrEquip
|
||||
public class CECIvtrWeapon : EC_IvtrEquip
|
||||
{
|
||||
//Attributes
|
||||
//Weapon essence data
|
||||
@@ -81,7 +81,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
/// Constructor for weapon item (cac loai vu khi)
|
||||
/// </summary>
|
||||
/// <param name="tid">Template id</param>
|
||||
public EC_IvtrWeapon(int tid, int expire_date) : base(tid, expire_date)
|
||||
public CECIvtrWeapon(int tid, int expire_date) : base(tid, expire_date)
|
||||
{
|
||||
m_iCID = (int)InventoryClassId.ICID_WEAPON;
|
||||
elementdataman pDB = ElementDataManProvider.GetElementDataMan();
|
||||
@@ -99,7 +99,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
RepairFee = m_pDBEssence.repairfee;
|
||||
ReputationReq = m_pDBEssence.require_reputation;
|
||||
}
|
||||
public EC_IvtrWeapon(EC_IvtrWeapon other) : base(other)
|
||||
public CECIvtrWeapon(CECIvtrWeapon other) : base(other)
|
||||
{
|
||||
m_pDBEssence = other.m_pDBEssence;
|
||||
m_pDBMajorType = other.m_pDBMajorType;
|
||||
@@ -436,5 +436,45 @@ namespace PerfectWorld.Scripts.Managers
|
||||
{
|
||||
return m_Essence.weapon_level;
|
||||
}
|
||||
|
||||
// Clone item
|
||||
public override EC_IvtrItem Clone()
|
||||
{
|
||||
return new CECIvtrWeapon(this);
|
||||
}
|
||||
|
||||
// Get equipment type
|
||||
public virtual int GetEquipmentType()
|
||||
{
|
||||
return 0; // EQUIP_WEAPON = 0
|
||||
}
|
||||
|
||||
// The weapon is range weapon ?
|
||||
public bool IsRangeWeapon()
|
||||
{
|
||||
return m_Essence.weapon_type == (int)WEAPON_TYPE.WEAPON_TYPE_RANGE;
|
||||
}
|
||||
|
||||
// Get essence data
|
||||
public IVTR_ESSENCE_WEAPON GetEssence()
|
||||
{
|
||||
return m_Essence;
|
||||
}
|
||||
|
||||
// Get database data
|
||||
public WEAPON_MAJOR_TYPE GetDBMajorType()
|
||||
{
|
||||
return m_pDBMajorType;
|
||||
}
|
||||
|
||||
public WEAPON_SUB_TYPE GetDBSubType()
|
||||
{
|
||||
return m_pDBSubType;
|
||||
}
|
||||
|
||||
public WEAPON_ESSENCE GetDBEssence()
|
||||
{
|
||||
return m_pDBEssence;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d252cb1fcb2e946688fd6836548fd0d4
|
||||
@@ -42,7 +42,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
if (edm == null) return CacheAndReturn(templateId, "");
|
||||
uint id = unchecked((uint)templateId);
|
||||
DATA_TYPE dATA_TYPE = default;
|
||||
object data = edm.get_data_ptr(id, ID_SPACE.ID_SPACE_ESSENCE,ref dATA_TYPE);
|
||||
object data = edm.get_data_ptr(id, ID_SPACE.ID_SPACE_ESSENCE, ref dATA_TYPE);
|
||||
string name = ExtractNameFromElement(data);
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
@@ -107,15 +107,15 @@ namespace BrewMonster.Scripts.Managers
|
||||
private Sprite LoadIconSpriteByKey(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key)) return null;
|
||||
|
||||
|
||||
// Load multi-sprite atlas if not already loaded
|
||||
if (_multiSpriteAtlas == null)
|
||||
{
|
||||
LoadMultiSpriteAtlas();
|
||||
}
|
||||
|
||||
|
||||
if (_multiSpriteAtlas == null) return null;
|
||||
|
||||
|
||||
// Try to find sprite by name in the atlas
|
||||
if (_spriteNameToIndexCache.TryGetValue(key, out var index))
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
return _multiSpriteAtlas[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fallback: try to find by name directly in the atlas
|
||||
foreach (var sprite in _multiSpriteAtlas)
|
||||
{
|
||||
@@ -133,7 +133,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Try lowercase/uppercase variants as fallback
|
||||
foreach (var sprite in _multiSpriteAtlas)
|
||||
{
|
||||
@@ -143,10 +143,10 @@ namespace BrewMonster.Scripts.Managers
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void LoadMultiSpriteAtlas()
|
||||
{
|
||||
try
|
||||
@@ -156,7 +156,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
if (atlasSprites != null && atlasSprites.Length > 0)
|
||||
{
|
||||
_multiSpriteAtlas = atlasSprites;
|
||||
|
||||
|
||||
// Build name-to-index cache for faster lookups
|
||||
_spriteNameToIndexCache.Clear();
|
||||
for (int i = 0; i < atlasSprites.Length; i++)
|
||||
@@ -166,7 +166,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
_spriteNameToIndexCache[atlasSprites[i].name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -280,7 +280,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
if (data == null) return "";
|
||||
var t = data.GetType();
|
||||
|
||||
|
||||
// Debug: Log all available fields and properties
|
||||
// Debug.Log($"[Inventory] Data type: {t.Name}");
|
||||
var fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||
@@ -301,7 +301,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
// Debug.Log($"[Inventory] Method: {m.Name} ({m.ReturnType.Name})");
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// Prefer decoding the raw fields first to control encoding (Unicode for Vietnamese),
|
||||
// then fall back to any string properties if needed.
|
||||
var fieldName = t.GetField("name", BindingFlags.Public | BindingFlags.Instance);
|
||||
@@ -313,7 +313,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
var rawData = string.Join(",", arr.Take(Math.Min(10, arr.Length)));
|
||||
}
|
||||
|
||||
|
||||
// Vietnamese names are stored as wide chars; decode as Unicode first.
|
||||
var s = ByteToStringUtils.UshortArrayToUnicodeString(arr);
|
||||
// Debug log to see what we're getting
|
||||
@@ -476,62 +476,62 @@ namespace BrewMonster.Scripts.Managers
|
||||
return hex;
|
||||
}
|
||||
|
||||
public int GetPileLimit(int templateId)
|
||||
{
|
||||
if (templateId <= 0) return 1;
|
||||
if (_pileLimitCache.TryGetValue(templateId, out var cached)) return cached;
|
||||
int limit = 1;
|
||||
try
|
||||
{
|
||||
var edm = ElementDataManProvider.GetElementDataMan();
|
||||
if (edm != null)
|
||||
{
|
||||
uint id = unchecked((uint)templateId);
|
||||
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
||||
object data = edm.get_data_ptr(id, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
|
||||
limit = ExtractPileLimitFromElement(data);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
if (limit <= 0) limit = 1;
|
||||
_pileLimitCache[templateId] = limit;
|
||||
return limit;
|
||||
}
|
||||
public int GetPileLimit(int templateId)
|
||||
{
|
||||
if (templateId <= 0) return 1;
|
||||
if (_pileLimitCache.TryGetValue(templateId, out var cached)) return cached;
|
||||
int limit = 1;
|
||||
try
|
||||
{
|
||||
var edm = ElementDataManProvider.GetElementDataMan();
|
||||
if (edm != null)
|
||||
{
|
||||
uint id = unchecked((uint)templateId);
|
||||
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
||||
object data = edm.get_data_ptr(id, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
|
||||
limit = ExtractPileLimitFromElement(data);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
if (limit <= 0) limit = 1;
|
||||
_pileLimitCache[templateId] = limit;
|
||||
return limit;
|
||||
}
|
||||
|
||||
private int ExtractPileLimitFromElement(object data)
|
||||
{
|
||||
if (data == null) return 1;
|
||||
var t = data.GetType();
|
||||
// Common field/property names across item essences
|
||||
string[] names = new[]
|
||||
{
|
||||
"pilelimit", "pile_limit", "pileLimit", "stack", "stack_max", "stackMax", "max_stack", "maxStack"
|
||||
};
|
||||
foreach (var name in names)
|
||||
{
|
||||
var f = t.GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if (f != null && (f.FieldType == typeof(int) || f.FieldType == typeof(uint) || f.FieldType == typeof(short) || f.FieldType == typeof(ushort) || f.FieldType == typeof(byte)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var val = f.GetValue(data);
|
||||
int limit = Convert.ToInt32(val);
|
||||
if (limit > 0) return limit;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
var p = t.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if (p != null && (p.PropertyType == typeof(int) || p.PropertyType == typeof(uint) || p.PropertyType == typeof(short) || p.PropertyType == typeof(ushort) || p.PropertyType == typeof(byte)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var val = p.GetValue(data, null);
|
||||
int limit = Convert.ToInt32(val);
|
||||
if (limit > 0) return limit;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
private int ExtractPileLimitFromElement(object data)
|
||||
{
|
||||
if (data == null) return 1;
|
||||
var t = data.GetType();
|
||||
// Common field/property names across item essences
|
||||
string[] names = new[]
|
||||
{
|
||||
"pilelimit", "pile_limit", "pileLimit", "stack", "stack_max", "stackMax", "max_stack", "maxStack"
|
||||
};
|
||||
foreach (var name in names)
|
||||
{
|
||||
var f = t.GetField(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if (f != null && (f.FieldType == typeof(int) || f.FieldType == typeof(uint) || f.FieldType == typeof(short) || f.FieldType == typeof(ushort) || f.FieldType == typeof(byte)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var val = f.GetValue(data);
|
||||
int limit = Convert.ToInt32(val);
|
||||
if (limit > 0) return limit;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
var p = t.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if (p != null && (p.PropertyType == typeof(int) || p.PropertyType == typeof(uint) || p.PropertyType == typeof(short) || p.PropertyType == typeof(ushort) || p.PropertyType == typeof(byte)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var val = p.GetValue(data, null);
|
||||
int limit = Convert.ToInt32(val);
|
||||
if (limit > 0) return limit;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -703,6 +703,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
public string m_strDesc = ""; // Item description
|
||||
public bool m_bIsInNPCPack; // true, this item is in NPC package
|
||||
public bool m_bLocalDetailData; // true, data from GetDetailDataFromLocal
|
||||
public int m_iCurEndurance; // Current endurance
|
||||
|
||||
public EC_Inventory m_pDescIvtr; // Inventory only used to get item description
|
||||
|
||||
@@ -735,6 +736,8 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_bIsInNPCPack = false;
|
||||
m_bLocalDetailData = false;
|
||||
m_pDescIvtr = null;
|
||||
m_iCurEndurance = 0;
|
||||
|
||||
}
|
||||
|
||||
public EC_IvtrItem(int tid, int expire_date)
|
||||
@@ -808,13 +811,13 @@ namespace BrewMonster.Scripts.Managers
|
||||
DATA_TYPE DataType = DATA_TYPE.DT_INVALID;
|
||||
object data = ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
|
||||
//Debug.Log("Create item data: DataType: " + DataType);
|
||||
switch(DataType)
|
||||
switch (DataType)
|
||||
{
|
||||
case DATA_TYPE.DT_WEAPON_ESSENCE:
|
||||
pItem = new EC_IvtrWeapon(tid, expire_date);
|
||||
pItem = new CECIvtrWeapon(tid, expire_date);
|
||||
break;
|
||||
case DATA_TYPE.DT_PROJECTILE_ESSENCE:
|
||||
pItem = new EC_IvtrArrow(tid, expire_date);
|
||||
pItem = new CECIvtrArrow(tid, expire_date);
|
||||
break;
|
||||
case DATA_TYPE.DT_ARMOR_ESSENCE:
|
||||
pItem = new EC_IvtrArmor(tid, expire_date);
|
||||
@@ -1258,6 +1261,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
#endregion
|
||||
|
||||
#region Simple property-style accessors (1:1 with C++)
|
||||
public int GetCurEndurance() { return m_iCurEndurance; }
|
||||
|
||||
public int GetClassID() => m_iCID;
|
||||
public int GetTemplateID() => m_tid;
|
||||
@@ -1342,7 +1346,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
//itemdataman* pItemDataMan = g_pGame->GetItemDataMan();
|
||||
object pData_temp = itemdataman.get_item_for_sell((uint)m_tid);
|
||||
if(pData_temp == null)
|
||||
if (pData_temp == null)
|
||||
{
|
||||
SetItemInfo(null, 0);
|
||||
SetLocalProps();
|
||||
@@ -1412,34 +1416,34 @@ namespace BrewMonster.Scripts.Managers
|
||||
}
|
||||
|
||||
// use specific color for the item price
|
||||
if((int)DescriptipionMsg.ITEMDESC_COL_WHITE == col)
|
||||
if ((int)DescriptipionMsg.ITEMDESC_COL_WHITE == col)
|
||||
{
|
||||
if( m_iPrice >= 100000000) // 100 million
|
||||
if (m_iPrice >= 100000000) // 100 million
|
||||
col = (int)DescriptipionMsg.ITEMDESC_COL_GREEN;
|
||||
else if ( m_iPrice >= 10000000) // 10 million
|
||||
else if (m_iPrice >= 10000000) // 10 million
|
||||
col = (int)DescriptipionMsg.ITEMDESC_COL_DARKGOLD;
|
||||
else if ( m_iPrice >= 1000000) // 1 million
|
||||
else if (m_iPrice >= 1000000) // 1 million
|
||||
col = (int)DescriptipionMsg.ITEMDESC_COL_YELLOW;
|
||||
}
|
||||
|
||||
|
||||
CECStringTab pDescTab = EC_Game.GetItemDesc();
|
||||
if (m_iScaleType == (int)ScaleType.SCALE_OFFLINESHOP)
|
||||
{
|
||||
AddDescText(col, false, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_PRICE));
|
||||
|
||||
string s1,s2;
|
||||
string s1, s2;
|
||||
BuildPriceNumberStr(m_iPrice, out s1);
|
||||
if (GetCount()>1)
|
||||
if (GetCount() > 1)
|
||||
{
|
||||
s2 = (m_iPrice * (long)GetCount()).ToString();
|
||||
s2 = (m_iPrice * (long)GetCount()).ToString();
|
||||
AddDescText(-1, false, " %s (%s)", s1, s2);
|
||||
}
|
||||
else
|
||||
AddDescText(-1, false, " %s", s1);
|
||||
AddDescText(-1, false, " %s", s1);
|
||||
}
|
||||
else if (m_iScaleType == (int)ScaleType.SCALE_BOOTH || m_tid == 21652) // 21651: yinpiao
|
||||
{
|
||||
string s1;
|
||||
string s1;
|
||||
BuildPriceNumberStr(m_iPrice, out s1);
|
||||
|
||||
AddDescText(col, false, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_UNITPRICE));
|
||||
@@ -1453,7 +1457,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
|
||||
AddDescText(col, false, pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_PRICE));
|
||||
AddDescText(-1, false, " %s (%s)", s1, s2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string s1;
|
||||
@@ -1644,7 +1648,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
var pDescTab = EC_Game.GetItemDesc();
|
||||
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
|
||||
int green = (int)DescriptipionMsg.ITEMDESC_COL2_BRIGHTBLUE; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
|
||||
|
||||
|
||||
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
|
||||
{
|
||||
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
|
||||
@@ -1675,7 +1679,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_DEAD_PROTECT);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_DEAD_PROTECT);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1685,7 +1689,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_DROP);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_DROP);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1695,7 +1699,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_TRADE);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_TRADE);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1705,7 +1709,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_PLAYER_TRADE);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_PLAYER_TRADE);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1715,7 +1719,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_LEAVE_SCENE_DISAPEAR);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_LEAVE_SCENE_DISAPEAR);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1725,7 +1729,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_USE_AFTER_PICK_UP);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_USE_AFTER_PICK_UP);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1735,7 +1739,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_DROP_WHEN_DEAD);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_DROP_WHEN_DEAD);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1745,7 +1749,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_DROP_WHEN_OFFLINE);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_DROP_WHEN_OFFLINE);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1755,7 +1759,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_UNREPAIRABLE);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_UNREPAIRABLE);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1765,7 +1769,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
m_strDesc += "\\r";
|
||||
if (pDescTab != null && pDescTab.IsInitialized())
|
||||
{
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_USER_TRASH);
|
||||
string desc = pDescTab.GetWideString((int)DescriptipionMsg.ITEMDESC_NO_USER_TRASH);
|
||||
if (!string.IsNullOrEmpty(desc))
|
||||
m_strDesc += desc;
|
||||
}
|
||||
@@ -1806,10 +1810,10 @@ namespace BrewMonster.Scripts.Managers
|
||||
protected void AddIDDescText()
|
||||
{
|
||||
// Optional: show internal id for debugging
|
||||
#if UNITY_EDITOR
|
||||
AddDescText(0, true, "ID: {0}", m_tid);
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
AddDescText(0, true, "ID: {0}", m_tid);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
protected void AddBindDescText()
|
||||
@@ -1944,7 +1948,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
public string GetExtendedDescText()
|
||||
{
|
||||
string result = string.Empty;
|
||||
|
||||
|
||||
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
|
||||
string szExtDesc = TryGetItemExtDesc();
|
||||
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
|
||||
|
||||
@@ -51,25 +51,25 @@ namespace BrewMonster.Scripts.Managers
|
||||
SIZE_GENERALCARD_EQUIPIVTR = SIZE_ALL_EQUIPIVTR - EQUIPIVTR_GENERALCARD1,
|
||||
}
|
||||
#region Inventory Essence Struct
|
||||
#pragma pack(1)
|
||||
#pragma pack(1)
|
||||
public struct IVTR_ESSENCE_WEAPON
|
||||
{
|
||||
public short weapon_type;
|
||||
public short weapon_dealy;
|
||||
public int weapon_class;
|
||||
public int weapon_level;
|
||||
public int require_projectile; // ��Ҫ��ҩ������
|
||||
public int damage_low; // ����������С��ֵ
|
||||
public int damage_high; // ������������ֵ
|
||||
public int magic_damage_low; // ħ������
|
||||
public int magic_damage_high; // ħ������
|
||||
// public int attack; // ������
|
||||
public int attack_speed;
|
||||
public float attack_range;
|
||||
public float attack_short_range;
|
||||
public IVTR_ESSENCE_WEAPON( byte[] data)
|
||||
public short weapon_type;
|
||||
public short weapon_dealy;
|
||||
public int weapon_class;
|
||||
public int weapon_level;
|
||||
public int require_projectile; // ��Ҫ��ҩ������
|
||||
public int damage_low; // ����������С��ֵ
|
||||
public int damage_high; // ������������ֵ
|
||||
public int magic_damage_low; // ħ������
|
||||
public int magic_damage_high; // ħ������
|
||||
// public int attack; // ������
|
||||
public int attack_speed;
|
||||
public float attack_range;
|
||||
public float attack_short_range;
|
||||
public IVTR_ESSENCE_WEAPON(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
weapon_type = dr.ReadShort();
|
||||
weapon_dealy = dr.ReadShort();
|
||||
weapon_class = dr.ReadInt();
|
||||
@@ -86,14 +86,14 @@ namespace BrewMonster.Scripts.Managers
|
||||
};
|
||||
public struct IVTR_ESSENCE_ARROW
|
||||
{
|
||||
public int dwBowMask;
|
||||
public int iDamage;
|
||||
public int iDamageScale;
|
||||
public int iWeaponReqLow;
|
||||
public int iWeaponReqHigh;
|
||||
public int dwBowMask;
|
||||
public int iDamage;
|
||||
public int iDamageScale;
|
||||
public int iWeaponReqLow;
|
||||
public int iWeaponReqHigh;
|
||||
public IVTR_ESSENCE_ARROW(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
dwBowMask = dr.ReadInt();
|
||||
iDamage = dr.ReadInt();
|
||||
iDamageScale = dr.ReadInt();
|
||||
@@ -103,20 +103,20 @@ namespace BrewMonster.Scripts.Managers
|
||||
};
|
||||
public struct IVTR_ESSENCE_DECORATION
|
||||
{
|
||||
public int damage;
|
||||
public int magic_damage;
|
||||
public int defense;
|
||||
public int armor;
|
||||
public int[] resistance;
|
||||
public int damage;
|
||||
public int magic_damage;
|
||||
public int defense;
|
||||
public int armor;
|
||||
public int[] resistance;
|
||||
public IVTR_ESSENCE_DECORATION(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
damage = dr.ReadInt();
|
||||
magic_damage = dr.ReadInt();
|
||||
defense = dr.ReadInt();
|
||||
armor = dr.ReadInt();
|
||||
resistance = new int[InventoryConst.NUM_MAGICCLASS];
|
||||
for(int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
|
||||
for (int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
|
||||
{
|
||||
resistance[i] = dr.ReadInt();
|
||||
}
|
||||
@@ -134,12 +134,12 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
Debug.Log("IVTR_ESSENCE_ARMOR: data.Length: " + data.Length);
|
||||
resistance = new int[InventoryConst.NUM_MAGICCLASS];
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
defense = dr.ReadInt();
|
||||
armor = dr.ReadInt();
|
||||
mp_enhance = dr.ReadInt();
|
||||
hp_enhance = dr.ReadInt();
|
||||
for(int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
|
||||
for (int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
|
||||
{
|
||||
resistance[i] = dr.ReadInt();
|
||||
}
|
||||
@@ -152,7 +152,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
public ushort gender;
|
||||
public IVTR_ESSENCE_FASHION(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
require_level = dr.ReadInt();
|
||||
color = dr.ReadUShort();
|
||||
gender = dr.ReadUShort();
|
||||
@@ -160,18 +160,18 @@ namespace BrewMonster.Scripts.Managers
|
||||
};
|
||||
public struct IVTR_ESSENCE_FLYSWORD
|
||||
{
|
||||
public int cur_time;
|
||||
public int max_time;
|
||||
public short require_level;
|
||||
public char level;
|
||||
public char improve_level;
|
||||
public int profession;
|
||||
public int time_per_element;
|
||||
public float speed_increase;
|
||||
public float speed_increase2;
|
||||
public int cur_time;
|
||||
public int max_time;
|
||||
public short require_level;
|
||||
public char level;
|
||||
public char improve_level;
|
||||
public int profession;
|
||||
public int time_per_element;
|
||||
public float speed_increase;
|
||||
public float speed_increase2;
|
||||
public IVTR_ESSENCE_FLYSWORD(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
cur_time = dr.ReadInt();
|
||||
max_time = dr.ReadInt();
|
||||
require_level = dr.ReadShort();
|
||||
@@ -193,46 +193,46 @@ namespace BrewMonster.Scripts.Managers
|
||||
};
|
||||
public struct IVTR_ESSENCE_AUTOHP
|
||||
{
|
||||
public int hp_left;
|
||||
public int hp_left;
|
||||
public float trigger;
|
||||
public IVTR_ESSENCE_AUTOHP(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
hp_left = dr.ReadInt();
|
||||
trigger = dr.ReadFloat();
|
||||
}
|
||||
};
|
||||
public struct IVTR_ESSENCE_AUTOMP
|
||||
{
|
||||
public int mp_left;
|
||||
public int mp_left;
|
||||
public float trigger;
|
||||
public IVTR_ESSENCE_AUTOMP(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
mp_left = dr.ReadInt();
|
||||
trigger = dr.ReadFloat();
|
||||
}
|
||||
};
|
||||
public struct IVTR_ESSENCE_PETEGG
|
||||
{
|
||||
public int req_level;
|
||||
public int req_class;
|
||||
public int honor_point;
|
||||
public int pet_tid;
|
||||
public int pet_vis_tid;
|
||||
public int pet_egg_tid;
|
||||
public int pet_class;
|
||||
public short level;
|
||||
public ushort color;
|
||||
public int exp;
|
||||
public int skill_point;
|
||||
public ushort name_len;
|
||||
public ushort skill_count;
|
||||
public int req_level;
|
||||
public int req_class;
|
||||
public int honor_point;
|
||||
public int pet_tid;
|
||||
public int pet_vis_tid;
|
||||
public int pet_egg_tid;
|
||||
public int pet_class;
|
||||
public short level;
|
||||
public ushort color;
|
||||
public int exp;
|
||||
public int skill_point;
|
||||
public ushort name_len;
|
||||
public ushort skill_count;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
public ushort[] name;
|
||||
public ushort[] name;
|
||||
public IVTR_ESSENCE_PETEGG(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
req_level = dr.ReadInt();
|
||||
req_class = dr.ReadInt();
|
||||
honor_point = dr.ReadInt();
|
||||
@@ -247,7 +247,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
name_len = dr.ReadUShort();
|
||||
skill_count = dr.ReadUShort();
|
||||
name = new ushort[8];
|
||||
for(int i = 0; i < 8; i++)
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
name[i] = dr.ReadUShort();
|
||||
}
|
||||
@@ -270,14 +270,14 @@ namespace BrewMonster.Scripts.Managers
|
||||
public short agility;
|
||||
public short vitality;
|
||||
public short energy;
|
||||
public short total_genius;
|
||||
public short[] genius ;
|
||||
public short total_genius;
|
||||
public short[] genius;
|
||||
public short refine_level;
|
||||
public int stamina;
|
||||
public int status_value;
|
||||
public int stamina;
|
||||
public int status_value;
|
||||
public _GOBLIN_DATA(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
exp = dr.ReadUInt();
|
||||
level = dr.ReadShort();
|
||||
total_attribute = dr.ReadShort();
|
||||
@@ -287,7 +287,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
energy = dr.ReadShort();
|
||||
total_genius = dr.ReadShort();
|
||||
genius = new short[5];
|
||||
for(int i = 0; i < 5; i++)
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
genius[i] = dr.ReadShort();
|
||||
}
|
||||
@@ -301,7 +301,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
public int skill_cnt;
|
||||
public IVTR_ESSENCE_GOBLIN(byte[] data)
|
||||
{
|
||||
CECDataReader dr = new (data, data.Length);
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
// Calculate size manually: uint(4) + 7*short(14) + short[5](10) + short(2) + 2*int(8) = 40 bytes
|
||||
const int GOBLIN_DATA_SIZE = 40;
|
||||
this.data = new _GOBLIN_DATA(dr.ReadData(GOBLIN_DATA_SIZE));
|
||||
@@ -357,8 +357,8 @@ namespace BrewMonster.Scripts.Managers
|
||||
// int exp;
|
||||
// int rebirth_times;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endregion
|
||||
public static class EC_IvtrType
|
||||
{
|
||||
@@ -728,9 +728,9 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
if (sub.id != armorSubTypeId) continue;
|
||||
uint mask = sub.equip_mask;
|
||||
|
||||
|
||||
// Check finger slots first - try to find an empty one
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0 ||
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0 ||
|
||||
(mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2)) != 0)
|
||||
{
|
||||
var availableFingerSlot = GetAvailableFingerSlot();
|
||||
@@ -739,7 +739,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
return availableFingerSlot;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For other slots, return the first matching one (original behavior)
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_HEAD)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_HEAD;
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_SHOULDER)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_SHOULDER;
|
||||
@@ -764,9 +764,9 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
if (sub.id != decorationSubTypeId) continue;
|
||||
uint mask = sub.equip_mask;
|
||||
|
||||
|
||||
// Check finger slots first - try to find an empty one
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0 ||
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0 ||
|
||||
(mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2)) != 0)
|
||||
{
|
||||
var availableFingerSlot = GetAvailableFingerSlot();
|
||||
@@ -775,7 +775,7 @@ namespace BrewMonster.Scripts.Managers
|
||||
return availableFingerSlot;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For other slots, return the first matching one (original behavior)
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_NECK)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_NECK;
|
||||
if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WAIST)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_WAIST;
|
||||
@@ -801,19 +801,25 @@ namespace BrewMonster.Scripts.Managers
|
||||
{
|
||||
return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1;
|
||||
}
|
||||
|
||||
|
||||
// Check if FINGER2 slot is empty
|
||||
var finger2Item = equipInv?.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2, false);
|
||||
if (finger2Item == null)
|
||||
{
|
||||
return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2;
|
||||
}
|
||||
|
||||
|
||||
// Both slots are occupied, return FINGER1 as fallback (original behavior)
|
||||
return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1;
|
||||
}
|
||||
}
|
||||
public enum InventoryType
|
||||
// Weapon type
|
||||
public enum WeaponType
|
||||
{
|
||||
WEAPONTYPE_MELEE = 0,
|
||||
WEAPONTYPE_RANGE = 1,
|
||||
};
|
||||
public enum InventoryType
|
||||
{
|
||||
IVTRTYPE_PACK = 0, // Normal pack
|
||||
IVTRTYPE_EQUIPPACK, // Equipment
|
||||
|
||||
@@ -194,7 +194,6 @@ namespace BrewMonster
|
||||
// iTotalTime: total cooling time, 0 means to use cooling time in database
|
||||
public void StartCooling(int iTotalTime, int iStartCnt)
|
||||
{
|
||||
BMLogger.LogError($"StartCooling iTotalTime={iTotalTime}, iStartCnt={iStartCnt}");
|
||||
m_iCoolTime = iTotalTime != 0 ? iTotalTime : GetCoreCoolingTime();
|
||||
m_iCoolCnt = iStartCnt;
|
||||
m_bCooling = true;
|
||||
@@ -302,6 +301,7 @@ namespace BrewMonster
|
||||
{
|
||||
return m_pSkillCore != null ? m_pSkillCore.GetCoolingTime() : 0;
|
||||
}
|
||||
public int GetComboSkPreSkill() { return m_pSkillCore.GetComboSkPreSkill(); }
|
||||
|
||||
public int GetExecuteTime()
|
||||
{
|
||||
|
||||
@@ -68,6 +68,10 @@ namespace BrewMonster
|
||||
NotifyObservers(&change);*/
|
||||
}
|
||||
}
|
||||
public ComboSkillState GetComboSkillState()
|
||||
{
|
||||
return m_comboSkillState;
|
||||
}
|
||||
void SetActiveComboSkills(Dictionary<uint, int> dic)
|
||||
{
|
||||
Dictionary<uint, int> newList = new Dictionary<uint, int>();
|
||||
@@ -112,6 +116,18 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool IsActiveComboSkill(uint skillID)
|
||||
{
|
||||
for (int i = 0; i < m_activeSkills.Count; i++)
|
||||
{
|
||||
if (m_activeSkills[i] == skillID && m_skillTimeOut[i] == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsComboPreSkill(uint skillID)
|
||||
{
|
||||
return m_preSkillSet.Contains(skillID);
|
||||
|
||||
@@ -951,6 +951,8 @@ namespace CSNetwork
|
||||
else if (ISNPCID(pCmd3.caster))
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_ENCHANTRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
case CommandID.HOST_STOP_SKILL:
|
||||
case CommandID.SELF_SKILL_INTERRUPTED:
|
||||
case CommandID.SKILL_PERFORM:
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
|
||||
@@ -759,7 +759,7 @@ namespace BrewMonster
|
||||
#region Placeholder Classes
|
||||
// These classes are referenced but not defined in the provided files
|
||||
// They should be implemented separately based on EC_Shortcut.h/cpp
|
||||
|
||||
[Serializable]
|
||||
public class CECShortcut
|
||||
{
|
||||
protected int m_iSCType;
|
||||
|
||||
@@ -2,13 +2,16 @@ using BrewMonster.Network;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using CSNetwork.GPDataType;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
/// <summary>
|
||||
/// Shortcut representing a combo skill group.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CECSCSkillGrp : CECShortcut
|
||||
{
|
||||
private int m_iGroupIdx;
|
||||
|
||||
@@ -100,8 +100,8 @@ namespace BrewMonster.Scripts.Skills
|
||||
public int move_env; //�ƶ����� // Movement environment
|
||||
public bool is_combat; //�Ƿ�ս��״̬ // Whether in combat state
|
||||
public int hp; //��ǰhp // Current HP
|
||||
public int max_hp; //���hp // Maximum HP
|
||||
// public ComboSkillState combo_state; //���������� // Combo skill state
|
||||
public int max_hp; //���hp // Maximum HP
|
||||
public ComboSkillState combo_state; //���������� // Combo skill state
|
||||
};
|
||||
|
||||
public struct GoblinUseRequirement
|
||||
@@ -242,11 +242,11 @@ namespace BrewMonster.Scripts.Skills
|
||||
}
|
||||
// ѧϰ����ȼ�?
|
||||
public virtual int GetRequiredRealmLevel() { return 0; }
|
||||
|
||||
|
||||
public virtual Dictionary<uint, int> GetRequiredSkill() => new Dictionary<uint, int>();
|
||||
|
||||
|
||||
public virtual int GetShowOrder() { return 0; }
|
||||
|
||||
|
||||
public virtual int SetLevel(int level) { return 0; }
|
||||
|
||||
public static int SetLevel(uint id, int level)
|
||||
@@ -398,13 +398,13 @@ namespace BrewMonster.Scripts.Skills
|
||||
skill = Skill.Create(id, ilevel);
|
||||
if (skill == null)
|
||||
return 5;
|
||||
|
||||
|
||||
int ret = 0;
|
||||
SkillWrapper wrapper = SkillWrapper.Instance;
|
||||
|
||||
if (wrapper.IsOverridden(id))
|
||||
return 11;
|
||||
|
||||
|
||||
int srank, prank;
|
||||
srank = skill.GetRank();
|
||||
prank = info.rank;
|
||||
@@ -424,17 +424,17 @@ namespace BrewMonster.Scripts.Skills
|
||||
ret = 4;
|
||||
else if (info.level < skill.GetRequiredLevel())
|
||||
ret = 2;
|
||||
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (info.sp < skill.GetRequiredSp())
|
||||
ret = 1;
|
||||
else if (info.money < skill.GetRequiredMoney())
|
||||
ret = 6;
|
||||
|
||||
|
||||
var pre_skills = skill.GetRequiredSkill();
|
||||
foreach (var kvp in pre_skills)
|
||||
{
|
||||
@@ -454,69 +454,70 @@ namespace BrewMonster.Scripts.Skills
|
||||
if (ability > 0 && wrapper.GetAbility(id) < ability)
|
||||
ret = 10;
|
||||
}
|
||||
|
||||
|
||||
if (info.realm_level < skill.GetRequiredRealmLevel())
|
||||
ret = 12;
|
||||
|
||||
return ret;
|
||||
}
|
||||
// 0:�ɹ� 1:����SP���� 2:�����츳�㲻��
|
||||
// 3:���� 4:���ܸ������� 5:����ID
|
||||
// 6:��Ǯ���� 7:��С���鼼�� 8:û�м�����
|
||||
// 9:�ȼ����� 10:���������� 11:ְҵ��ƥ��
|
||||
// 12:���������㣬��ְҵ��ƥ��
|
||||
// 3:���� 4:���ܸ������� 5:����ID
|
||||
// 6:��Ǯ���� 7:��С���鼼�� 8:û�м�����
|
||||
// 9:�ȼ����� 10:���������� 11:ְҵ��ƥ��
|
||||
// 12:���������㣬��ְҵ��ƥ��
|
||||
public static int GoblinLearn(uint id, GoblinRequirement info, int level)
|
||||
{
|
||||
Skill s = Skill.Create(id, level);
|
||||
if(s == null)
|
||||
if (s == null)
|
||||
return 5;
|
||||
if(level<1 || level> s.GetMaxLevel())
|
||||
if (level < 1 || level > s.GetMaxLevel())
|
||||
return 3;
|
||||
if(s.GetCls() != 258)
|
||||
if (s.GetCls() != 258)
|
||||
return 7;
|
||||
|
||||
|
||||
int ret = 0;
|
||||
|
||||
int[] iReqGen = new int[5] {0, 0, 0, 0, 0};
|
||||
|
||||
int[] iReqGen = new int[5] { 0, 0, 0, 0, 0 };
|
||||
int iReqLevel = s.GetRequiredLevel();
|
||||
// iReqLevelΪ7λ�����������λΪ�ȼ���ǰ5λΪ�츳���������λΪ��
|
||||
int iLevelRequirement = iReqLevel%100;
|
||||
if(info.level < iLevelRequirement)
|
||||
int iLevelRequirement = iReqLevel % 100;
|
||||
if (info.level < iLevelRequirement)
|
||||
return 9;
|
||||
|
||||
iReqLevel /= 100;
|
||||
|
||||
int i;
|
||||
for(i=0;i<5;i++)
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
iReqGen[4-i] = iReqLevel%10;
|
||||
iReqGen[4 - i] = iReqLevel % 10;
|
||||
iReqLevel /= 10;
|
||||
}
|
||||
|
||||
for(i=0;i<5;i++)
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
if(info.genius[i] < iReqGen[4-i])
|
||||
if (info.genius[i] < iReqGen[4 - i])
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(info.sp < s.GetRequiredSp())
|
||||
|
||||
if (info.sp < s.GetRequiredSp())
|
||||
ret = 1;
|
||||
//else if(info.money<s->GetRequiredMoney(id, level))
|
||||
// ret = 6;
|
||||
|
||||
if(info.mp < s.GetMpCost() &&
|
||||
if (info.mp < s.GetMpCost() &&
|
||||
((s.GetCls() != 0) && (((1 << info.profession) & s.GetCls()) == 0)))
|
||||
ret = 12;
|
||||
else if(info.mp < s.GetMpCost())
|
||||
else if (info.mp < s.GetMpCost())
|
||||
ret = 10;
|
||||
else if((s.GetCls() != 0) && (((1 << info.profession) & s.GetCls()) == 0))
|
||||
else if ((s.GetCls() != 0) && (((1 << info.profession) & s.GetCls()) == 0))
|
||||
ret = 11;
|
||||
|
||||
return ret;
|
||||
}
|
||||
public virtual int GetComboSkPreSkill() { return 0; }
|
||||
public static int GetComboSkPreSkill(uint id)
|
||||
{
|
||||
SkillStub s = SkillStub.GetStub(id);
|
||||
SkillStub s = SkillStub.GetStub(id);
|
||||
if (s != null)
|
||||
{
|
||||
return s.combosk_preskill;
|
||||
|
||||
@@ -0,0 +1,820 @@
|
||||
# Flow: CreateSkillGroupShortcut - C++ Implementation
|
||||
|
||||
## Overview
|
||||
This document describes the complete flow for creating a skill combo group shortcut in the C++ codebase. A skill group shortcut (`CECSCSkillGrp`) represents a combo skill sequence that can be placed in the quick bar and executed with a single click.
|
||||
|
||||
---
|
||||
|
||||
## Entry Points
|
||||
|
||||
### 1. **From UI Drag & Drop** (DlgDragDrop.cpp:603)
|
||||
When a user drags a combo skill icon from the combo skill panel to a shortcut slot:
|
||||
|
||||
```cpp
|
||||
// DlgDragDrop.cpp:595-604
|
||||
if (strstr(pObjSrc->GetName(), "Img_ConSkill")) // Combo skill icon
|
||||
{
|
||||
int nCombo = pObjSrc->GetData(); // Get combo group index (1-based)
|
||||
int iSlot = atoi(pObjOver->GetName() + strlen("Item_")); // Get target slot (1-based)
|
||||
|
||||
CECShortcutSet *pSCS = CECGameUIMan::GetSCSByDlg(pDlgOver->GetName());
|
||||
if (!pSCS->GetShortcut(iSlot-1) || !g_pGame->GetConfigs()->GetGameSettings().bLockQuickBar)
|
||||
pSCS->CreateSkillGroupShortcut(iSlot - 1, nCombo - 1); // Convert to 0-based
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. User drags combo skill icon (`Img_ConSkill`) to shortcut slot
|
||||
2. Extract combo group index from source object data (1-based)
|
||||
3. Extract target slot number from destination object name (1-based)
|
||||
4. Get the appropriate `CECShortcutSet` based on dialog
|
||||
5. Check if slot is empty or quick bar is unlocked
|
||||
6. Call `CreateSkillGroupShortcut` with 0-based indices
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis: UI Drag & Drop Flow
|
||||
|
||||
### **Step 1: Combo Skill Icon Creation & Data Initialization**
|
||||
|
||||
**Location:** `DlgSkillSubOther.cpp:71-99` - `UpdateComboSkill()`
|
||||
|
||||
This function is called when the skill dialog is shown (`OnShowDialog()` at line 193) and creates/updates all combo skill icons:
|
||||
|
||||
```cpp
|
||||
// DlgSkillSubOther.cpp:71-99
|
||||
void CDlgSkillSubOther::UpdateComboSkill() {
|
||||
EC_VIDEO_SETTING setting = GetGame()->GetConfigs()->GetVideoSettings();
|
||||
|
||||
// Loop through all possible combo skill groups (typically 8-12)
|
||||
for(int i = 0; i < EC_COMBOSKILL_NUM; i++)
|
||||
{
|
||||
// Create icon name: "Img_ConSkill01", "Img_ConSkill02", etc.
|
||||
AString strName;
|
||||
strName.Format("Img_ConSkill%02d", i + 1);
|
||||
|
||||
// Get the image picture object from dialog
|
||||
PAUIIMAGEPICTURE pImage = static_cast<PAUIIMAGEPICTURE>(GetDlgItem(strName));
|
||||
if( pImage )
|
||||
{
|
||||
// Check if this combo skill group is configured (has an icon)
|
||||
if( setting.comboSkill[i].nIcon != 0 )
|
||||
{
|
||||
// Set the icon image from sprite sheet
|
||||
pImage->SetCover(
|
||||
GetGameUIMan()->m_pA2DSpriteIcons[CECGameUIMan::ICONS_SKILLGRP],
|
||||
setting.comboSkill[i].nIcon + 1);
|
||||
|
||||
// ⭐ KEY: Store the combo group index (1-based) in the icon's data
|
||||
// i is 0-based (0, 1, 2, ...), so i+1 is 1-based (1, 2, 3, ...)
|
||||
pImage->SetData(i + 1);
|
||||
|
||||
// Set a flag pointer (just indicates this is a valid combo skill)
|
||||
pImage->SetDataPtr((void*)1);
|
||||
|
||||
// Set tooltip/hint text
|
||||
ACString strText;
|
||||
strText.Format(GetStringFromTable(804), i);
|
||||
pImage->SetHint(strText);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Empty/invalid combo skill - clear the icon
|
||||
pImage->SetCover(NULL, -1);
|
||||
pImage->SetData(0); // No data = invalid
|
||||
pImage->SetDataPtr(NULL);
|
||||
pImage->SetHint(_AL(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- **Icon Naming:** Icons are named `"Img_ConSkill%02d"` where `%02d` is the 1-based index (01, 02, 03, ...)
|
||||
- **Data Storage:** The combo group index is stored via `SetData(i + 1)`:
|
||||
- `i` is 0-based array index (0, 1, 2, ...)
|
||||
- `i + 1` is 1-based group number (1, 2, 3, ...)
|
||||
- This 1-based value is what gets retrieved during drag & drop
|
||||
- **Validation:** If `nIcon == 0`, the combo skill doesn't exist, so `SetData(0)` marks it as invalid
|
||||
- **Icon Source:** Icon image comes from `ICONS_SKILLGRP` sprite sheet at index `nIcon + 1`
|
||||
|
||||
---
|
||||
|
||||
### **Step 2: User Initiates Drag Operation**
|
||||
|
||||
**Location:** `DlgSkillSubOther.cpp:219-232` - `OnEventLButtonDownCombo()`
|
||||
|
||||
When user clicks and holds on a combo skill icon:
|
||||
|
||||
```cpp
|
||||
// DlgSkillSubOther.cpp:219-232
|
||||
void CDlgSkillSubOther::OnEventLButtonDownCombo(WPARAM wParam, LPARAM lParam, AUIObject * pObj) {
|
||||
// ⭐ Validation: Check if icon has valid data (combo skill exists)
|
||||
if( pObj->GetData() == 0 )
|
||||
return; // No combo skill configured, can't drag
|
||||
|
||||
// Get viewport coordinates
|
||||
A3DVIEWPORTPARAM *p = m_pA3DEngine->GetActiveViewport()->GetParam();
|
||||
POINT pt =
|
||||
{
|
||||
GET_X_LPARAM(lParam) - p->X,
|
||||
GET_Y_LPARAM(lParam) - p->Y,
|
||||
};
|
||||
|
||||
// Store drag start position
|
||||
GetGameUIMan()->m_ptLButtonDown = pt;
|
||||
|
||||
// ⭐ Start drag & drop operation
|
||||
// This passes the source object (pObj) which contains the data
|
||||
GetGameUIMan()->InvokeDragDrop(this, pObj, pt);
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- **Event Mapping:** The event is mapped via `AUI_ON_EVENT("Img_ConSkill*", WM_LBUTTONDOWN, OnEventLButtonDownCombo)` (line 19)
|
||||
- **Validation:** Checks `GetData() == 0` to ensure combo skill exists before allowing drag
|
||||
- **Data Preservation:** The `pObj` (icon object) is passed to `InvokeDragDrop()`, preserving the data stored in Step 1
|
||||
|
||||
---
|
||||
|
||||
### **Step 3: Drag & Drop Processing**
|
||||
|
||||
**Location:** `DlgDragDrop.cpp:79-127` - `OnEventLButtonUp()`
|
||||
|
||||
When user releases mouse button, the drag & drop system processes the drop:
|
||||
|
||||
```cpp
|
||||
// DlgDragDrop.cpp:79-127 (simplified)
|
||||
void CDlgDragDrop::OnEventLButtonUp(WPARAM wParam, LPARAM lParam, AUIObject *pObj)
|
||||
{
|
||||
// Get the source object that was being dragged
|
||||
PAUIOBJECT pObjSrc = (PAUIOBJECT)pItem->GetDataPtr("ptr_AUIObject");
|
||||
if( !pObjSrc )
|
||||
return;
|
||||
|
||||
// Get destination (where mouse was released)
|
||||
PAUIDIALOG pDlgOver;
|
||||
PAUIOBJECT pObjOver;
|
||||
// ... hit test to find what's under the mouse ...
|
||||
GetGameUIMan()->HitTest(x, y, &pDlgOver, &pObjOver, this);
|
||||
|
||||
// Route to appropriate handler based on source dialog
|
||||
// ... routing logic ...
|
||||
|
||||
// For skill-related drags, calls:
|
||||
OnSkillDragDrop(pDlgSrc, pObjSrc, pDlgOver, pObjOver, pIvtrSrc);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Step 4: Skill Drag & Drop Handler**
|
||||
|
||||
**Location:** `DlgDragDrop.cpp:589-627` - `OnSkillDragDrop()`
|
||||
|
||||
This is where the combo skill icon data is extracted and used:
|
||||
|
||||
```cpp
|
||||
// DlgDragDrop.cpp:589-627
|
||||
void CDlgDragDrop::OnSkillDragDrop(PAUIDIALOG pDlgSrc, PAUIOBJECT pObjSrc,
|
||||
PAUIDIALOG pDlgOver, PAUIOBJECT pObjOver, CECIvtrItem* pIvtrSrc)
|
||||
{
|
||||
// Check if dropping on quick bar dialog
|
||||
if( pDlgOver && strstr(pDlgOver->GetName(), "Win_Quickbar")
|
||||
&& pObjOver && strstr(pObjOver->GetName(), "Item_") )
|
||||
{
|
||||
// ⭐ Check if source is a combo skill icon
|
||||
if( strstr(pObjSrc->GetName(), "Img_ConSkill") )
|
||||
{
|
||||
// ⭐ Extract combo group index from icon's stored data (1-based)
|
||||
int nCombo = pObjSrc->GetData(); // Returns the value set in UpdateComboSkill()
|
||||
|
||||
// Extract target slot number from destination object name
|
||||
// Destination name format: "Item_1", "Item_2", etc. (1-based)
|
||||
int iSlot = atoi(pObjOver->GetName() + strlen("Item_")); // "Item_1" -> 1
|
||||
|
||||
// Get the shortcut set for this dialog
|
||||
CECShortcutSet *pSCS = CECGameUIMan::GetSCSByDlg(pDlgOver->GetName());
|
||||
|
||||
// Check if slot is empty or quick bar is unlocked
|
||||
if( !pSCS->GetShortcut(iSlot-1) ||
|
||||
!g_pGame->GetConfigs()->GetGameSettings().bLockQuickBar )
|
||||
{
|
||||
// ⭐ Create skill group shortcut
|
||||
// Convert both indices to 0-based:
|
||||
// - iSlot: 1-based -> 0-based (slot 1 -> index 0)
|
||||
// - nCombo: 1-based -> 0-based (group 1 -> index 0)
|
||||
pSCS->CreateSkillGroupShortcut(iSlot - 1, nCombo - 1);
|
||||
}
|
||||
}
|
||||
// Handle regular skill drag (not combo)
|
||||
else
|
||||
{
|
||||
CECSkill *pSkill = (CECSkill *)pObjSrc->GetDataPtr("ptr_CECSkill");
|
||||
// ... create regular skill shortcut ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- **Icon Identification:** `strstr(pObjSrc->GetName(), "Img_ConSkill")` identifies combo skill icons
|
||||
- **Data Retrieval:** `pObjSrc->GetData()` retrieves the 1-based group index stored in Step 1
|
||||
- **Slot Extraction:** Destination slot is extracted from object name `"Item_N"` where N is 1-based
|
||||
- **Index Conversion:** Both indices are converted from 1-based to 0-based before calling `CreateSkillGroupShortcut()`
|
||||
|
||||
---
|
||||
|
||||
### **Complete Data Flow Diagram**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 1: Icon Creation (UpdateComboSkill) │
|
||||
│ Location: DlgSkillSubOther.cpp:71-99 │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ For each combo skill group (i=0..N): │
|
||||
│ │
|
||||
│ 1. Create icon name: │
|
||||
│ "Img_ConSkill%02d" (i+1) │
|
||||
│ │
|
||||
│ 2. If combo exists (nIcon != 0): │
|
||||
│ - Set icon image │
|
||||
│ - ⭐ SetData(i + 1) │
|
||||
│ (stores 1-based group index) │
|
||||
│ - SetDataPtr((void*)1) │
|
||||
│ - Set hint text │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ Icon Object Created │
|
||||
│ - Name: "Img_ConSkill01", etc. │
|
||||
│ - Data: 1, 2, 3, ... (1-based) │
|
||||
│ - DataPtr: (void*)1 │
|
||||
│ - Icon: Sprite sheet image │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
│ (User clicks and drags)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 2: Drag Initiation (OnEventLButtonDownCombo) │
|
||||
│ Location: DlgSkillSubOther.cpp:219-232 │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 1. Validate: GetData() != 0 │
|
||||
│ 2. Get mouse position │
|
||||
│ 3. InvokeDragDrop(this, pObj, pt) │
|
||||
│ (pObj contains the data!) │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
│ (User drags to quick bar)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 3: Drop Processing (OnEventLButtonUp) │
|
||||
│ Location: DlgDragDrop.cpp:79-127 │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 1. Get source object (pObjSrc) │
|
||||
│ 2. Hit test to find destination │
|
||||
│ 3. Route to OnSkillDragDrop() │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ STEP 4: Skill Drag Handler (OnSkillDragDrop) │
|
||||
│ Location: DlgDragDrop.cpp:589-627 │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 1. Check: strstr(name, "Img_ConSkill")│
|
||||
│ 2. ⭐ Extract: nCombo = GetData() │
|
||||
│ (retrieves 1-based group index) │
|
||||
│ 3. Extract: iSlot from "Item_N" │
|
||||
│ 4. Get shortcut set │
|
||||
│ 5. Validate slot/quick bar │
|
||||
│ 6. CreateSkillGroupShortcut( │
|
||||
│ iSlot-1, nCombo-1) │
|
||||
│ (convert to 0-based) │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ CreateSkillGroupShortcut() │
|
||||
│ Creates CECSCSkillGrp with │
|
||||
│ group index (0-based) │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Data Storage Summary**
|
||||
|
||||
| Component | Storage Method | Value Type | Example |
|
||||
|-----------|---------------|------------|---------|
|
||||
| **Combo Group Index** | `AUIObject::SetData(int)` | 1-based integer | 1, 2, 3, ... |
|
||||
| **Icon Name** | Object name | String pattern | "Img_ConSkill01", "Img_ConSkill02" |
|
||||
| **Icon Image** | `SetCover()` | Sprite sheet index | `nIcon + 1` |
|
||||
| **Validation Flag** | `SetDataPtr((void*)1)` | Pointer flag | `(void*)1` = valid, `NULL` = invalid |
|
||||
|
||||
**Important Notes:**
|
||||
1. **The icon DOES contain the data needed** - it's stored via `SetData(i + 1)` during icon creation
|
||||
2. **The data is 1-based** - stored as 1, 2, 3, ... but converted to 0-based (0, 1, 2, ...) when creating the shortcut
|
||||
3. **Data is validated** - `GetData() == 0` means no combo skill, so drag is prevented
|
||||
4. **Icon name pattern** - `"Img_ConSkill*"` is used to identify combo skill icons during drag & drop
|
||||
|
||||
---
|
||||
|
||||
### 2. **From Config Data Loading** (EC_ShortcutSet.cpp:600-616)
|
||||
When loading shortcut configuration from server/save file:
|
||||
|
||||
```cpp
|
||||
// EC_ShortcutSet.cpp:600-616
|
||||
case CECShortcut::SCT_SKILLGRP:
|
||||
{
|
||||
if (dwVer >= 3) // Version check - skill groups added in version 3
|
||||
{
|
||||
int iGroupIdx = *(int*)pData; // Read group index from config data
|
||||
pData += sizeof(int);
|
||||
|
||||
if (iGroupIdx >= 0)
|
||||
CreateSkillGroupShortcut(iSlot, iGroupIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Read shortcut type from config data
|
||||
2. If type is `SCT_SKILLGRP` and version >= 3:
|
||||
- Read group index from binary data
|
||||
- Validate group index (>= 0)
|
||||
- Call `CreateSkillGroupShortcut`
|
||||
|
||||
---
|
||||
|
||||
### 3. **From AssignSkillGrpShortcut** (EC_HostPlayer.cpp:7934-7941)
|
||||
When assigning skill group shortcuts from configuration array:
|
||||
|
||||
```cpp
|
||||
// EC_HostPlayer.cpp:7934-7941
|
||||
void CECHostPlayer::AssignSkillGrpShortcut(
|
||||
const std::vector<SkillGrpShortCutConfig> & skillGrpSCConfigArray,
|
||||
CECShortcutSet** aSCSets)
|
||||
{
|
||||
std::vector<SkillGrpShortCutConfig>::const_iterator it;
|
||||
for (it = skillGrpSCConfigArray.begin(); it != skillGrpSCConfigArray.end(); it++)
|
||||
{
|
||||
if(it->groupIndex != -1) // -1 means invalid/empty
|
||||
aSCSets[it->setNum]->CreateSkillGroupShortcut(it->slotNum, it->groupIndex);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Iterate through skill group shortcut configuration array
|
||||
2. For each valid config (groupIndex != -1):
|
||||
- Get the appropriate shortcut set by `setNum`
|
||||
- Call `CreateSkillGroupShortcut` with slot and group index
|
||||
|
||||
**Note:** Before calling this, `ConvertSkillGrpShortcut` is called to validate combo skills exist:
|
||||
```cpp
|
||||
// EC_HostPlayer.cpp:7915-7923
|
||||
void CECHostPlayer::ConvertSkillGrpShortcut(std::vector<SkillGrpShortCutConfig> & skillGrpSCConfigArray)
|
||||
{
|
||||
std::vector<SkillGrpShortCutConfig>::iterator it;
|
||||
for (it = skillGrpSCConfigArray.begin(); it != skillGrpSCConfigArray.end(); it++)
|
||||
{
|
||||
EC_VIDEO_SETTING vs = g_pGame->GetConfigs()->GetVideoSettings();
|
||||
if (vs.comboSkill[it->groupIndex].nIcon == 0) // Combo skill doesn't exist
|
||||
it->groupIndex = -1; // Mark as invalid
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Implementation
|
||||
|
||||
### **CreateSkillGroupShortcut** (EC_ShortcutSet.cpp:135-150)
|
||||
|
||||
```cpp
|
||||
// EC_ShortcutSet.cpp:135-150
|
||||
bool CECShortcutSet::CreateSkillGroupShortcut(int iSlot, int iGroupIdx)
|
||||
{
|
||||
// 1. Create new CECSCSkillGrp object
|
||||
CECSCSkillGrp* pSkillGrpSC = new CECSCSkillGrp;
|
||||
if (!pSkillGrpSC)
|
||||
return false;
|
||||
|
||||
// 2. Initialize with group index
|
||||
if (!pSkillGrpSC->Init(iGroupIdx))
|
||||
{
|
||||
delete pSkillGrpSC;
|
||||
a_LogOutput(1, "CECShortcutSet::CreateSkillGroupShortcut, Failed to initialize skill group shortcut");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Set shortcut in slot (replaces any existing shortcut at this slot)
|
||||
SetShortcut(iSlot, pSkillGrpSC);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. **Allocate** new `CECSCSkillGrp` object
|
||||
2. **Initialize** with group index
|
||||
3. **Set** shortcut in the specified slot (automatically releases old shortcut if exists)
|
||||
|
||||
---
|
||||
|
||||
## CECSCSkillGrp Class
|
||||
|
||||
### **Class Definition** (EC_Shortcut.h:282-309)
|
||||
|
||||
```cpp
|
||||
class CECSCSkillGrp : public CECShortcut
|
||||
{
|
||||
public:
|
||||
CECSCSkillGrp();
|
||||
CECSCSkillGrp(const CECSCSkillGrp& src);
|
||||
virtual ~CECSCSkillGrp() {}
|
||||
|
||||
// Initialize object
|
||||
bool Init(int iGroupIdx);
|
||||
|
||||
// Virtual functions from CECShortcut
|
||||
virtual CECShortcut* Clone();
|
||||
virtual bool Execute(); // Executes the combo skill
|
||||
virtual const char* GetIconFile();
|
||||
virtual const wchar_t* GetDesc();
|
||||
virtual int GetCoolTime(int* piMax=NULL);
|
||||
|
||||
private:
|
||||
int m_iGroupIdx; // Combo skill group index (0-based)
|
||||
CECString m_strDesc; // Description text
|
||||
};
|
||||
```
|
||||
|
||||
### **Initialization** (EC_Shortcut.cpp:696-703)
|
||||
|
||||
```cpp
|
||||
// EC_Shortcut.cpp:696-703
|
||||
bool CECSCSkillGrp::Init(int iGroupIdx)
|
||||
{
|
||||
m_iGroupIdx = iGroupIdx; // Store group index
|
||||
|
||||
// Format description: "Skill Group {index}"
|
||||
CECStringTab* pStrTab = g_pGame->GetItemDesc();
|
||||
m_strDesc.Format(pStrTab->GetWideString(CMDDESC_SKILLGROUP), m_iGroupIdx);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Store group index
|
||||
2. Format description string from string table
|
||||
3. Return success
|
||||
|
||||
### **Execution** (EC_Shortcut.cpp:712-717)
|
||||
|
||||
```cpp
|
||||
// EC_Shortcut.cpp:712-717
|
||||
bool CECSCSkillGrp::Execute()
|
||||
{
|
||||
CECHostPlayer* pHost = g_pGame->GetGameRun()->GetHostPlayer();
|
||||
pHost->ApplyComboSkill(m_iGroupIdx); // Apply combo skill with stored group index
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Get host player instance
|
||||
2. Call `ApplyComboSkill` with the stored group index
|
||||
3. This triggers the combo skill sequence execution
|
||||
|
||||
---
|
||||
|
||||
## ApplyComboSkill Flow
|
||||
|
||||
### **ApplyComboSkill** (EC_HostPlayer.cpp:7710-7737)
|
||||
|
||||
```cpp
|
||||
// EC_HostPlayer.cpp:7710-7737
|
||||
bool CECHostPlayer::ApplyComboSkill(int iGroup, bool bIgnoreAtkLoop, int iForceAtk)
|
||||
{
|
||||
a_LogOutput(1, "HoangDEv: ApplyComboSkill - Applying combo skill, group=%d, target=%d, ignoreAtkLoop=%d, forceAtk=%d",
|
||||
iGroup, m_idSelTarget, bIgnoreAtkLoop, iForceAtk);
|
||||
|
||||
// 1. Clear current combo skill if exists
|
||||
ClearComboSkill();
|
||||
|
||||
// 2. Create new combo skill object
|
||||
if (!(m_pComboSkill = new CECComboSkill))
|
||||
{
|
||||
ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Initialize combo skill
|
||||
bool bForceAttack = (iForceAtk != 0);
|
||||
if (!(m_pComboSkill->Init(this, iGroup, m_idSelTarget, bForceAttack, bIgnoreAtkLoop)))
|
||||
{
|
||||
delete m_pComboSkill;
|
||||
m_pComboSkill = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Start combo skill execution
|
||||
m_pComboSkill->Continue(m_bMelee);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. **Clear** any existing combo skill
|
||||
2. **Create** new `CECComboSkill` object
|
||||
3. **Initialize** with:
|
||||
- Host player (this)
|
||||
- Group index
|
||||
- Target ID
|
||||
- Force attack flag
|
||||
- Ignore attack loop flag
|
||||
4. **Start** combo skill execution with `Continue()`
|
||||
|
||||
---
|
||||
|
||||
## Combo Skill Data Structure
|
||||
|
||||
### **EC_COMBOSKILL Structure**
|
||||
|
||||
Combo skills are stored in `EC_VIDEO_SETTING`:
|
||||
|
||||
```cpp
|
||||
// From configs/video settings
|
||||
struct EC_COMBOSKILL
|
||||
{
|
||||
int nIcon; // Icon ID (0 = empty/invalid)
|
||||
int idSkill[EC_COMBOSKILL_LEN]; // Array of skill IDs in combo sequence
|
||||
// ... other fields
|
||||
};
|
||||
|
||||
EC_VIDEO_SETTING vs;
|
||||
vs.comboSkill[EC_COMBOSKILL_NUM]; // Array of combo skill groups
|
||||
```
|
||||
|
||||
**Constants:**
|
||||
- `EC_COMBOSKILL_NUM`: Number of combo skill groups (typically 8-12)
|
||||
- `EC_COMBOSKILL_LEN`: Maximum number of skills in a combo sequence
|
||||
|
||||
### **Combo Skill Initialization** (EC_ComboSkill.cpp:74-92)
|
||||
|
||||
```cpp
|
||||
// EC_ComboSkill.cpp:74-92
|
||||
bool CECComboSkill::Init(CECHostPlayer* pHost, int iGroup, int idTarget,
|
||||
bool bForceAttack, bool bIgnoreAtkLoop)
|
||||
{
|
||||
// 1. Validate group index
|
||||
if (iGroup < 0 || iGroup >= EC_COMBOSKILL_NUM)
|
||||
{
|
||||
ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Store parameters
|
||||
m_pHost = pHost;
|
||||
m_iGroup = iGroup;
|
||||
m_iCursor = 0; // Start at first skill in combo
|
||||
m_bStop = false;
|
||||
m_idTarget = idTarget;
|
||||
m_bForceAtk = bForceAttack;
|
||||
m_bIgnoreAtkLoop = bIgnoreAtkLoop;
|
||||
|
||||
// 3. Load combo skill data from config
|
||||
CECConfigs* pCfg = g_pGame->GetConfigs();
|
||||
m_cs = pCfg->GetVideoSettings().comboSkill[iGroup];
|
||||
|
||||
// 4. Find loop start position (if any)
|
||||
// ... (finds SID_LOOPSTART marker)
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ USER ACTION / CONFIG LOAD │
|
||||
└──────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────┐
|
||||
│ Entry Point Selection │
|
||||
└──────────┬───────────────────┘
|
||||
│
|
||||
┌──────────┴──────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────────────┐
|
||||
│ UI Drag&Drop │ │ LoadConfigData │
|
||||
│ (DlgDragDrop)│ │ (EC_ShortcutSet) │
|
||||
└──────┬───────┘ └──────────┬───────────┘
|
||||
│ │
|
||||
└──────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ CreateSkillGroupShortcut │
|
||||
│ (EC_ShortcutSet) │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ new CECSCSkillGrp │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ CECSCSkillGrp::Init(iGroup) │
|
||||
│ - Store group index │
|
||||
│ - Format description │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ SetShortcut(iSlot, pSC) │
|
||||
│ - Replace old shortcut │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ SHORTCUT CREATED │
|
||||
│ Ready for execution │
|
||||
└─────────────────────────────┘
|
||||
│
|
||||
│ (When user clicks shortcut)
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ CECSCSkillGrp::Execute() │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ ApplyComboSkill(iGroup) │
|
||||
│ (EC_HostPlayer) │
|
||||
└──────────┬─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ new CECComboSkill │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ CECComboSkill::Init() │
|
||||
│ - Load combo data from config│
|
||||
│ - Set cursor to start │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ CECComboSkill::Continue() │
|
||||
│ - Execute first skill │
|
||||
│ - Chain to next skills │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Data Structures
|
||||
|
||||
### **SkillGrpShortCutConfig**
|
||||
|
||||
```cpp
|
||||
struct SkillGrpShortCutConfig
|
||||
{
|
||||
int setNum; // Shortcut set number (which quick bar)
|
||||
int slotNum; // Slot index within the set (0-based)
|
||||
int groupIndex; // Combo skill group index (0-based, -1 = invalid)
|
||||
};
|
||||
```
|
||||
|
||||
### **CECSCSkillGrp Members**
|
||||
|
||||
```cpp
|
||||
class CECSCSkillGrp : public CECShortcut
|
||||
{
|
||||
int m_iGroupIdx; // Combo skill group index (0-based)
|
||||
CECString m_strDesc; // Description: "Skill Group {index}"
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation & Error Handling
|
||||
|
||||
### **Group Index Validation**
|
||||
|
||||
1. **In CreateSkillGroupShortcut:**
|
||||
- No explicit validation (assumes caller validates)
|
||||
- `Init()` may fail if group index is invalid
|
||||
|
||||
2. **In ConvertSkillGrpShortcut:**
|
||||
```cpp
|
||||
if (vs.comboSkill[it->groupIndex].nIcon == 0)
|
||||
it->groupIndex = -1; // Mark as invalid
|
||||
```
|
||||
|
||||
3. **In CECComboSkill::Init:**
|
||||
```cpp
|
||||
if (iGroup < 0 || iGroup >= EC_COMBOSKILL_NUM)
|
||||
{
|
||||
ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
### **Slot Validation**
|
||||
|
||||
- Slot index must be within bounds of shortcut set size
|
||||
- `SetShortcut()` handles slot bounds checking internally
|
||||
|
||||
---
|
||||
|
||||
## Related Functions
|
||||
|
||||
### **ClearComboSkill** (EC_HostPlayer.cpp:7740-7747)
|
||||
|
||||
```cpp
|
||||
void CECHostPlayer::ClearComboSkill()
|
||||
{
|
||||
if (m_pComboSkill)
|
||||
{
|
||||
delete m_pComboSkill;
|
||||
m_pComboSkill = NULL;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **GetGroupIndex** (EC_Shortcut.h)
|
||||
|
||||
```cpp
|
||||
int GetGroupIndex() const { return m_iGroupIdx; }
|
||||
```
|
||||
|
||||
Used to retrieve the group index from a shortcut for UI display or validation.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**CreateSkillGroupShortcut Flow:**
|
||||
|
||||
1. **Entry Points:**
|
||||
- UI drag & drop operation
|
||||
- Config data loading from server/save
|
||||
- Programmatic assignment from config array
|
||||
|
||||
2. **Core Process:**
|
||||
- Create `CECSCSkillGrp` object
|
||||
- Initialize with group index
|
||||
- Store in shortcut set at specified slot
|
||||
|
||||
3. **Execution:**
|
||||
- When shortcut is clicked, `Execute()` is called
|
||||
- Calls `ApplyComboSkill()` on host player
|
||||
- Creates and initializes `CECComboSkill` object
|
||||
- Starts combo skill sequence execution
|
||||
|
||||
4. **Data Source:**
|
||||
- Combo skill definitions stored in `EC_VIDEO_SETTING.comboSkill[]`
|
||||
- Each combo group contains array of skill IDs
|
||||
- Group index (0-based) references position in array
|
||||
|
||||
---
|
||||
|
||||
## Conversion Notes for C#
|
||||
|
||||
When converting to C#:
|
||||
- Replace `new`/`delete` with C# object allocation
|
||||
- Use `List<>` instead of `std::vector`
|
||||
- Use `string` instead of `CECString`
|
||||
- Use nullable types for optional values
|
||||
- Use `IDisposable` pattern for cleanup
|
||||
- Consider using events/delegates for shortcut execution
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3fdcd3d58e16de40b0541be0d654c69
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -89,6 +89,7 @@ namespace BrewMonster.Scripts.Skills
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
public override int GetComboSkPreSkill() { return stub.combosk_preskill; }
|
||||
public override byte GetType() { return stub.type; }
|
||||
public override int GetCommonCoolDown() { return stub.commoncooldown; }
|
||||
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using BrewMonster.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay;
|
||||
using BrewMonster.Network;
|
||||
|
||||
namespace BrewMonster.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Skill dialog sub-other panel - displays combo skills, fixed skills, item skills, and produce skills
|
||||
/// Converted from DlgSkillSubOther.cpp/h
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class CDlgSkillSubOther : AUIDialog
|
||||
{
|
||||
private const int ITEM_SKILL_MAX_COUNT = 8;
|
||||
private const int FIXED_SKILL_MAX_COUNT = 4;
|
||||
|
||||
[Header("Combo Skill Images")]
|
||||
[SerializeField] private List<AUIImagePicture> m_comboSkillImages = new List<AUIImagePicture>();
|
||||
|
||||
[Header("Fixed Skill Components")]
|
||||
[SerializeField] private List<AUIImagePicture> m_fixedImgPics = new List<AUIImagePicture>();
|
||||
[SerializeField] private List<TextMeshProUGUI> m_fixedTxts = new List<TextMeshProUGUI>();
|
||||
|
||||
[Header("Item Skill Images")]
|
||||
[SerializeField] private List<AUIImagePicture> m_itemSkillImages = new List<AUIImagePicture>();
|
||||
|
||||
[Header("Produce Skill Components")]
|
||||
[SerializeField] private List<AUIImagePicture> m_produceImgIcons = new List<AUIImagePicture>();
|
||||
[SerializeField] private List<TextMeshProUGUI> m_produceNameLabels = new List<TextMeshProUGUI>();
|
||||
[SerializeField] private List<TextMeshProUGUI> m_produceSkilledTxtLabels = new List<TextMeshProUGUI>();
|
||||
[SerializeField] private List<TextMeshProUGUI> m_produceSkilledExpLabels = new List<TextMeshProUGUI>();
|
||||
[SerializeField] private List<TextMeshProUGUI> m_produceLevelLabels = new List<TextMeshProUGUI>();
|
||||
|
||||
[Header("Buttons")]
|
||||
[SerializeField] private Button m_btnEdit;
|
||||
[SerializeField] private Button m_btnDelete;
|
||||
[SerializeField] private Button m_btnNew;
|
||||
|
||||
private int m_nComboSelect = 0;
|
||||
|
||||
private readonly List<uint> m_fixedSkills = new List<uint>();
|
||||
private readonly List<uint> m_produceSkills = new List<uint>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Initialize fixed skills - 167 is return skill
|
||||
m_fixedSkills.Add(167);
|
||||
|
||||
// Initialize produce skills - respectively: weapon crafting, armor crafting, accessory crafting, alchemy
|
||||
m_produceSkills.Add(158);
|
||||
m_produceSkills.Add(159);
|
||||
m_produceSkills.Add(160);
|
||||
m_produceSkills.Add(161);
|
||||
|
||||
// Setup button listeners
|
||||
if (m_btnEdit != null)
|
||||
m_btnEdit.onClick.AddListener(OnCommandEdit);
|
||||
if (m_btnDelete != null)
|
||||
m_btnDelete.onClick.AddListener(OnCommandDelete);
|
||||
if (m_btnNew != null)
|
||||
m_btnNew.onClick.AddListener(OnCommandNew);
|
||||
}
|
||||
|
||||
public override void OnShowDialogue()
|
||||
{
|
||||
base.OnShowDialogue();
|
||||
Debug.Log("CDlgSkillSubOther::OnShowDialog()");
|
||||
UpdateComboSkill();
|
||||
UpdateFixedSkill();
|
||||
UpdateItemSkill();
|
||||
UpdateProduceSkill();
|
||||
}
|
||||
|
||||
/*public override bool Render()
|
||||
{
|
||||
if (!base.Render())
|
||||
return false;
|
||||
|
||||
if (!gameObject.activeInHierarchy)
|
||||
return true;
|
||||
|
||||
// Item skills and produce skills may change, update them
|
||||
UpdateItemSkill();
|
||||
UpdateProduceSkill();
|
||||
|
||||
// Update fixed skill cooldowns
|
||||
CECHostPlayer host = GetHostPlayer();
|
||||
for (int i = 0; i < m_fixedSkills.Count && i < m_fixedImgPics.Count; i++)
|
||||
{
|
||||
if (m_fixedImgPics[i] != null && host != null)
|
||||
{
|
||||
CECSkill pSkill = host.GetPositiveSkillByID(m_fixedSkills[i]);
|
||||
UpdateImagePictureCD(m_fixedImgPics[i], pSkill);
|
||||
}
|
||||
}
|
||||
|
||||
// Update item skill cooldowns
|
||||
int equipSkillNum = host != null ? host.GetEquipSkillNum() : 0;
|
||||
for (int i = 0; i < equipSkillNum && i < ITEM_SKILL_MAX_COUNT && i < m_itemSkillImages.Count; i++)
|
||||
{
|
||||
if (m_itemSkillImages[i] != null && host != null)
|
||||
{
|
||||
CECSkill pSkill = host.GetEquipSkillByIndex(i);
|
||||
UpdateImagePictureCD(m_itemSkillImages[i], pSkill);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}*/
|
||||
|
||||
// Edit combo skill - called from DlgSkill
|
||||
public void OnCommandEdit()
|
||||
{
|
||||
// TODO: Implement DlgSkillEdit equivalent
|
||||
// GetGameUIMan()->m_pDlgSkillEdit->SetData(m_nComboSelect);
|
||||
// GetGameUIMan()->m_pDlgSkillEdit->Show(true);
|
||||
Debug.Log($"OnCommandEdit: combo select = {m_nComboSelect}");
|
||||
}
|
||||
|
||||
// New combo skill - called from DlgSkill
|
||||
public void OnCommandNew()
|
||||
{
|
||||
// TODO: Implement DlgSkillEdit equivalent
|
||||
// GetGameUIMan()->m_pDlgSkillEdit->SetData(0);
|
||||
// GetGameUIMan()->m_pDlgSkillEdit->Show(true);
|
||||
Debug.Log("OnCommandNew");
|
||||
}
|
||||
|
||||
// Delete combo skill - called from DlgSkill
|
||||
public void OnCommandDelete()
|
||||
{
|
||||
if (m_nComboSelect < 0 || m_nComboSelect > EC_ConfigConstants.EC_COMBOSKILL_NUM)
|
||||
return;
|
||||
|
||||
CECConfigs configs = EC_Game.GetConfigs();
|
||||
if (configs == null)
|
||||
return;
|
||||
|
||||
EC_VIDEO_SETTING setting = configs.GetVideoSettings();
|
||||
setting.comboSkill[m_nComboSelect - 1].nIcon = 0;
|
||||
m_nComboSelect = 0;
|
||||
configs.SetVideoSettings(setting);
|
||||
UpdateComboSkill();
|
||||
}
|
||||
|
||||
// Helper dictionary to store combo skill data
|
||||
private Dictionary<AUIImagePicture, uint> m_comboSkillData = new Dictionary<AUIImagePicture, uint>();
|
||||
|
||||
// Helper dictionary to store skill data for images
|
||||
private Dictionary<AUIImagePicture, CECSkill> m_skillData = new Dictionary<AUIImagePicture, CECSkill>();
|
||||
|
||||
// Update combo skill icons - called from DlgSkill
|
||||
public void UpdateComboSkill()
|
||||
{
|
||||
CECConfigs configs = EC_Game.GetConfigs();
|
||||
if (configs == null)
|
||||
return;
|
||||
|
||||
EC_VIDEO_SETTING setting = configs.GetVideoSettings();
|
||||
CECGameUIMan gameUIMan = GetGameUIMan();
|
||||
|
||||
for (int i = 0; i < EC_ConfigConstants.EC_COMBOSKILL_NUM; i++)
|
||||
{
|
||||
AUIImagePicture pImage = m_comboSkillImages[i];
|
||||
if (pImage != null)
|
||||
{
|
||||
if (setting.comboSkill[i].nIcon != 0)
|
||||
{
|
||||
// TODO: Set icon from sprite sheet
|
||||
//pImage->SetCover(GetGameUIMan()->m_pA2DSpriteIcons[CECGameUIMan::ICONS_SKILLGRP],
|
||||
// setting.comboSkill[i].nIcon + 1);
|
||||
m_comboSkillData[pImage] = (uint)(i + 1);
|
||||
//pImage.SetDataPtr(new object(), "ptr_Valid"); // Equivalent to (void*)1
|
||||
|
||||
string hintText = GetStringFromTable(804);
|
||||
if (!string.IsNullOrEmpty(hintText))
|
||||
{
|
||||
hintText = string.Format(hintText, i);
|
||||
// TODO: Set hint text
|
||||
// pImage->SetHint(hintText);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Clear icon
|
||||
// pImage->SetCover(NULL, -1);
|
||||
m_comboSkillData[pImage] = 0;
|
||||
pImage.SetDataPtr(null);
|
||||
// pImage->SetHint("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update fixed skill display
|
||||
public void UpdateFixedSkill()
|
||||
{
|
||||
// First hide all fixed skill components
|
||||
/*for (int i = 0; i < FIXED_SKILL_MAX_COUNT; i++)
|
||||
{
|
||||
if (i < m_fixedImgPics.Count && m_fixedImgPics[i] != null)
|
||||
m_fixedImgPics[i].gameObject.SetActive(false);
|
||||
if (i < m_fixedTxts.Count && m_fixedTxts[i] != null)
|
||||
m_fixedTxts[i].gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
Debug.Assert(m_fixedSkills.Count <= FIXED_SKILL_MAX_COUNT, "Fixed skills count exceeds max");
|
||||
|
||||
CECHostPlayer host = GetHostPlayer();
|
||||
for (int i = 0; i < m_fixedSkills.Count; i++)
|
||||
{
|
||||
if (i < m_fixedImgPics.Count && m_fixedImgPics[i] != null)
|
||||
m_fixedImgPics[i].gameObject.SetActive(true);
|
||||
if (i < m_fixedTxts.Count && m_fixedTxts[i] != null)
|
||||
m_fixedTxts[i].gameObject.SetActive(true);
|
||||
|
||||
CECSkill pSkill = host?.GetPositiveSkillByID(m_fixedSkills[i]);
|
||||
if (pSkill != null)
|
||||
{
|
||||
SetImage(m_fixedImgPics[i], pSkill);
|
||||
if (i < m_fixedTxts.Count && m_fixedTxts[i] != null)
|
||||
m_fixedTxts[i].text = pSkill.GetNameDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < m_fixedImgPics.Count && m_fixedImgPics[i] != null)
|
||||
{
|
||||
m_fixedImgPics[i].gameObject.SetActive(false);
|
||||
SetImage(m_fixedImgPics[i], null);
|
||||
}
|
||||
if (i < m_fixedTxts.Count && m_fixedTxts[i] != null)
|
||||
m_fixedTxts[i].gameObject.SetActive(false);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// Update item skill display
|
||||
public void UpdateItemSkill()
|
||||
{
|
||||
CECHostPlayer host = GetHostPlayer();
|
||||
if (host == null)
|
||||
return;
|
||||
|
||||
int equipSkillNum = host.GetEquipSkillNum();
|
||||
for (int i = 0; i < ITEM_SKILL_MAX_COUNT && i < m_itemSkillImages.Count; i++)
|
||||
{
|
||||
AUIImagePicture pImgPic = m_itemSkillImages[i];
|
||||
if (pImgPic != null)
|
||||
{
|
||||
if (i < equipSkillNum)
|
||||
{
|
||||
CECSkill pSkill = host.GetEquipSkillByIndex(i);
|
||||
SetImage(pImgPic, pSkill);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetImage(pImgPic, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update produce skill display
|
||||
public void UpdateProduceSkill()
|
||||
{
|
||||
CECHostPlayer host = GetHostPlayer();
|
||||
if (host == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < m_produceSkills.Count; i++)
|
||||
{
|
||||
AUIImagePicture imgIcon = i < m_produceImgIcons.Count ? m_produceImgIcons[i] : null;
|
||||
TextMeshProUGUI lblName = i < m_produceNameLabels.Count ? m_produceNameLabels[i] : null;
|
||||
TextMeshProUGUI lblSkilledTxt = i < m_produceSkilledTxtLabels.Count ? m_produceSkilledTxtLabels[i] : null;
|
||||
TextMeshProUGUI lblSkilledExp = i < m_produceSkilledExpLabels.Count ? m_produceSkilledExpLabels[i] : null;
|
||||
TextMeshProUGUI lblLevel = i < m_produceLevelLabels.Count ? m_produceLevelLabels[i] : null;
|
||||
|
||||
if (imgIcon != null)
|
||||
imgIcon.gameObject.SetActive(true);
|
||||
if (lblName != null)
|
||||
lblName.gameObject.SetActive(true);
|
||||
|
||||
//CECSkill pSkill = host.GetPassiveSkillByID(m_produceSkills[i]);
|
||||
/* if (pSkill == null)
|
||||
{
|
||||
if (lblSkilledTxt != null)
|
||||
lblSkilledTxt.gameObject.SetActive(false);
|
||||
if (lblSkilledExp != null)
|
||||
lblSkilledExp.gameObject.SetActive(false);
|
||||
if (lblLevel != null)
|
||||
lblLevel.gameObject.SetActive(false);
|
||||
|
||||
if (imgIcon != null)
|
||||
{
|
||||
// Set gray color
|
||||
Image img = imgIcon.GetComponent<Image>();
|
||||
if (img != null)
|
||||
img.color = new Color(0.5f, 0.5f, 0.5f, 1f); // RGB(128, 128, 128)
|
||||
}
|
||||
|
||||
CECSkill tmpSkill = new CECSkill(m_produceSkills[i], 1);
|
||||
SetImage(imgIcon, tmpSkill);
|
||||
if (lblName != null)
|
||||
lblName.text = tmpSkill.GetNameDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lblSkilledTxt != null)
|
||||
lblSkilledTxt.gameObject.SetActive(true);
|
||||
if (lblSkilledExp != null)
|
||||
lblSkilledExp.gameObject.SetActive(true);
|
||||
if (lblLevel != null)
|
||||
lblLevel.gameObject.SetActive(true);
|
||||
|
||||
if (imgIcon != null)
|
||||
{
|
||||
// Set white color
|
||||
Image img = imgIcon.GetComponent<Image>();
|
||||
if (img != null)
|
||||
img.color = Color.white; // RGB(255, 255, 255)
|
||||
}
|
||||
|
||||
SetImage(imgIcon, pSkill);
|
||||
if (lblName != null)
|
||||
lblName.text = pSkill.GetNameDisplay();*/
|
||||
|
||||
/* int maxAbility = ElementSkill.GetMaxAbility(m_produceSkills[i], pSkill.GetSkillLevel());
|
||||
int ability = ElementSkill.GetAbility(m_produceSkills[i]);*/
|
||||
|
||||
/* if (lblSkilledExp != null)
|
||||
lblSkilledExp.text = $"{ability}/{maxAbility}";*/
|
||||
/*if (lblLevel != null)
|
||||
{
|
||||
string levelFormat = GetStringFromTable(11323);
|
||||
if (!string.IsNullOrEmpty(levelFormat))
|
||||
lblLevel.text = string.Format(levelFormat, pSkill.GetSkillLevel());
|
||||
}*/
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// Get combo skill data for an image
|
||||
private uint GetComboSkillData(AUIImagePicture pImage)
|
||||
{
|
||||
if (pImage == null)
|
||||
return 0;
|
||||
return m_comboSkillData.TryGetValue(pImage, out uint data) ? data : 0;
|
||||
}
|
||||
|
||||
// Handle combo skill icon click for drag - called from DlgSkill
|
||||
public void OnEventLButtonDownCombo(AUIImagePicture pObj)
|
||||
{
|
||||
uint data = GetComboSkillData(pObj);
|
||||
if (pObj == null || data == 0)
|
||||
return;
|
||||
|
||||
// TODO: Implement drag and drop
|
||||
// A3DVIEWPORTPARAM *p = m_pA3DEngine->GetActiveViewport()->GetParam();
|
||||
// POINT pt = { GET_X_LPARAM(lParam) - p->X, GET_Y_LPARAM(lParam) - p->Y };
|
||||
// GetGameUIMan()->m_ptLButtonDown = pt;
|
||||
// GetGameUIMan()->InvokeDragDrop(this, pObj, pt);
|
||||
Debug.Log($"OnEventLButtonDownCombo: combo skill {data}");
|
||||
}
|
||||
|
||||
// Handle fixed skill icon click for drag
|
||||
public void OnEventLButtonDownFixed(AUIImagePicture pObj)
|
||||
{
|
||||
if (pObj == null)
|
||||
return;
|
||||
|
||||
/* CECSkill skill = GetSkillFromImage(pObj);
|
||||
if (skill == null)
|
||||
return;*/
|
||||
|
||||
// TODO: Implement drag and drop
|
||||
// GetGameUIMan()->m_ptLButtonDown = ...;
|
||||
// GetGameUIMan()->InvokeDragDrop(this, pObj, GetGameUIMan()->m_ptLButtonDown);
|
||||
Debug.Log("OnEventLButtonDownFixed");
|
||||
}
|
||||
|
||||
// Handle item skill icon click for drag
|
||||
public void OnEventLButtonDownItem(AUIImagePicture pObj)
|
||||
{
|
||||
if (pObj == null)
|
||||
return;
|
||||
|
||||
/* CECSkill skill = GetSkillFromImage(pObj);
|
||||
if (skill == null)
|
||||
return;*/
|
||||
|
||||
// TODO: Implement drag and drop
|
||||
// GetGameUIMan()->m_ptLButtonDown = ...;
|
||||
// GetGameUIMan()->InvokeDragDrop(this, pObj, GetGameUIMan()->m_ptLButtonDown);
|
||||
Debug.Log("OnEventLButtonDownItem");
|
||||
}
|
||||
|
||||
// Select combo skill - called from DlgSkill
|
||||
public void SelectComboSkill(int n)
|
||||
{
|
||||
if (n < 1 || n > m_comboSkillImages.Count)
|
||||
return;
|
||||
|
||||
if (m_nComboSelect == n)
|
||||
{
|
||||
// Deselect
|
||||
AUIImagePicture pImage = m_comboSkillImages[n - 1];
|
||||
if (pImage != null)
|
||||
{
|
||||
Image img = pImage.GetComponent<Image>();
|
||||
if (img != null)
|
||||
img.color = Color.white; // RGB(255, 255, 255)
|
||||
}
|
||||
m_nComboSelect = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Deselect previous
|
||||
if (m_nComboSelect != 0 && m_nComboSelect <= m_comboSkillImages.Count)
|
||||
{
|
||||
AUIImagePicture pImage = m_comboSkillImages[m_nComboSelect - 1];
|
||||
if (pImage != null)
|
||||
{
|
||||
Image img = pImage.GetComponent<Image>();
|
||||
if (img != null)
|
||||
img.color = Color.white; // RGB(255, 255, 255)
|
||||
}
|
||||
}
|
||||
|
||||
// Select new
|
||||
m_nComboSelect = n;
|
||||
AUIImagePicture pNewImage = m_comboSkillImages[n - 1];
|
||||
if (pNewImage != null)
|
||||
{
|
||||
Image img = pNewImage.GetComponent<Image>();
|
||||
if (img != null)
|
||||
img.color = new Color(0.627f, 0.627f, 0.627f, 1f); // RGB(160, 160, 160)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set image for an AUIImagePicture with a skill - called from DlgSkill
|
||||
public void SetImage(AUIImagePicture pImage, CECSkill pSkill)
|
||||
{
|
||||
if (pImage == null)
|
||||
return;
|
||||
|
||||
if (pSkill != null)
|
||||
{
|
||||
// TODO: Set icon from sprite sheet
|
||||
// AString strFile;
|
||||
// af_GetFileTitle(pSkill->GetIconFile(), strFile);
|
||||
// strFile.MakeLower();
|
||||
// pImage->SetCover(
|
||||
// GetGameUIMan()->m_pA2DSpriteIcons[CECGameUIMan::ICONS_SKILL],
|
||||
// GetGameUIMan()->m_IconMap[CECGameUIMan::ICONS_SKILL][strFile]);
|
||||
|
||||
// Store skill in dictionary for retrieval
|
||||
m_skillData[pImage] = pSkill;
|
||||
|
||||
// Try to set as shortcut if possible
|
||||
/* if (pSkill is CECShortcut shortcut)
|
||||
pImage.SetDataPtr(shortcut, "ptr_CECSkill");*/
|
||||
// TODO: Set hint
|
||||
// pImage->SetHint(pSkill->GetDesc());
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Clear icon
|
||||
// pImage->SetCover(NULL, -1);
|
||||
m_skillData.Remove(pImage);
|
||||
pImage.SetDataPtr(null);
|
||||
// pImage->SetHint("");
|
||||
}
|
||||
}
|
||||
|
||||
// Get skill from image
|
||||
/* private CECSkill GetSkillFromImage(AUIImagePicture pImage)
|
||||
{
|
||||
if (pImage == null)
|
||||
return null;
|
||||
|
||||
// Try dictionary first
|
||||
if (m_skillData.TryGetValue(pImage, out CECSkill skill))
|
||||
return skill;
|
||||
|
||||
// Try shortcut
|
||||
CECShortcut shortcut = pImage.GetDataPtr();
|
||||
return shortcut as CECSkill;
|
||||
}*/
|
||||
|
||||
// Update image picture cooldown display
|
||||
private void UpdateImagePictureCD(AUIImagePicture pImgPic, CECSkill pSkill)
|
||||
{
|
||||
if (pImgPic == null)
|
||||
return;
|
||||
|
||||
CECHostPlayer pHost = GetHostPlayer();
|
||||
if (pHost == null)
|
||||
return;
|
||||
|
||||
AUIClockIcon pClock = pImgPic.GetClockIcon();
|
||||
if (pClock == null)
|
||||
return;
|
||||
|
||||
Image img = pImgPic.GetComponent<Image>();
|
||||
if (img == null)
|
||||
return;
|
||||
|
||||
if (pSkill != null && pSkill.ReadyToCast() && pHost.GetPrepSkill() != pSkill)
|
||||
{
|
||||
if (pHost.CheckSkillCastCondition(pSkill) == 0)
|
||||
img.color = Color.white; // RGB(255, 255, 255)
|
||||
else
|
||||
img.color = new Color(0.5f, 0.5f, 0.5f, 1f); // RGB(128, 128, 128)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set clock color
|
||||
Image clockImg = pClock.GetClockIcon();
|
||||
if (clockImg != null)
|
||||
clockImg.color = new Color(0, 0, 0, 0.5f); // RGBA(0, 0, 0, 128)
|
||||
}
|
||||
|
||||
if (pSkill != null && (pSkill.GetCoolingTime() > 0 || pHost.GetPrepSkill() == pSkill))
|
||||
{
|
||||
pClock.SetProgressRange(0, pSkill.GetCoolingTime());
|
||||
if (pHost.GetPrepSkill() == pSkill)
|
||||
pClock.SetProgressPos(0);
|
||||
else
|
||||
pClock.SetProgressPos(pSkill.GetCoolingTime() - pSkill.GetCoolingCnt());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27624565938535e4593764faffe78bbf
|
||||
@@ -12,8 +12,8 @@ namespace BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay
|
||||
{
|
||||
public class AUIImagePicture : MonoBehaviour
|
||||
{
|
||||
CECShortcut pSC;
|
||||
[Header("AUIImagePicture")]
|
||||
[SerializeField] CECShortcut pSC;
|
||||
[SerializeField] Button skillbutton;
|
||||
[SerializeField] protected Image skillImage;
|
||||
[SerializeField] GameObject borderImage;
|
||||
@@ -28,10 +28,11 @@ namespace BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay
|
||||
}
|
||||
skillbutton.onClick.AddListener(Execute);
|
||||
}
|
||||
public void SetDataPtr(CECShortcut pvData, string strName)
|
||||
public void SetDataPtr(CECShortcut pvData, string strName = null)
|
||||
{
|
||||
pSC = pvData;
|
||||
}
|
||||
public CECShortcut GetDataPtr() => pSC;
|
||||
public void Execute()
|
||||
{
|
||||
if (pSC != null)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay;
|
||||
//#define Applyforalicense
|
||||
|
||||
using BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
@@ -23,6 +25,225 @@ namespace BrewMonster
|
||||
/// Apply for a license remove later
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
|
||||
|
||||
|
||||
#if !Applyforalicense
|
||||
public bool UpdateShortcuts()
|
||||
{
|
||||
CECShortcut pSC;
|
||||
Image skillImage;
|
||||
CECSCSkill pSCSkill;
|
||||
int iIconFile, nMax;
|
||||
AUIImagePicture pCell;
|
||||
CECSkill pSkill = new CECSkill(-1, -1);
|
||||
AUIClockIcon pClock;
|
||||
|
||||
|
||||
int nCurPanel9 = GetCurPanel1();
|
||||
int nCurPanel8 = GetCurPanel2();
|
||||
|
||||
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
|
||||
if (pHost == null) return false;
|
||||
var a_pSCS = new List<CECShortcutSet>();
|
||||
var a_pszPanel = new List<string>();
|
||||
GetQuickBarNameAndSC(pHost, a_pszPanel, a_pSCS, nCurPanel9, nCurPanel8);
|
||||
|
||||
for (int i = 0; i <= 1/*(int)a_pSCS.Count*/; i++)
|
||||
{
|
||||
if (a_pSCS[i] == null)
|
||||
continue;
|
||||
|
||||
//*//*CDlgQuickBar* pQuickBar = dynamic_cast<CDlgQuickBar*>(GetGameUIMan()->GetDialog(a_pszPanel[i]));
|
||||
//if (!pQuickBar || !pQuickBar->IsShow()) continue;*//*
|
||||
|
||||
for (int j = 0; j < AUIImagePictureList.Count; j++)
|
||||
{
|
||||
pCell = AUIImagePictureList[j];
|
||||
pSC = a_pSCS[i].GetShortcut(j);
|
||||
pClock = pCell.GetClockIcon();
|
||||
pClock.SetProgressRange(0, 1);
|
||||
pClock.SetProgressPos(1);
|
||||
if (pSC != null)
|
||||
{
|
||||
if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_SKILL)
|
||||
{
|
||||
iIconFile = (int)EC_GAMEUI_ICONS.ICONS_SKILL;
|
||||
pSCSkill = (CECSCSkill)pSC;
|
||||
pSkill = pSCSkill.GetSkill();
|
||||
if (false/*m_bDelGoblinSkillSC && GNET::ElementSkill::IsGoblinSkill(pSkill->GetSkillID())*/)
|
||||
{
|
||||
/* a_pSCS[i]->SetShortcut(j, NULL);
|
||||
pSC = NULL;*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pSkill != null && pSkill.ReadyToCast() && pHost.GetPrepSkill() != pSkill)
|
||||
{
|
||||
if (ElementSkill.IsGoblinSkill((uint)pSkill.GetSkillID()))
|
||||
{
|
||||
/* if (pHostGoblin && !pHostGoblin->CheckSkillCastCondition(pSkill))
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pHost.CheckSkillCastCondition(pSkill) == 0)
|
||||
{
|
||||
//pCell.SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
//pCell.SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*else
|
||||
pClock.SetColor(A3DCOLORRGBA(0, 0, 0, 128));*/
|
||||
if (pSkill != null && (pSkill.GetCoolingTime() > 0 ||
|
||||
pHost.GetPrepSkill() == pSkill))
|
||||
{
|
||||
pClock.SetProgressRange(0, pSkill.GetCoolingTime());
|
||||
if (pHost.GetPrepSkill() == pSkill)
|
||||
{
|
||||
pClock.SetProgressPos(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
pClock.SetProgressPos(pSkill.GetCoolingTime() - pSkill.GetCoolingCnt());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*else if (pSC->GetType() == CECShortcut::SCT_ITEM)
|
||||
{
|
||||
iIconFile = CECGameUIMan::ICONS_INVENTORY;
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
pSCItem = (CECSCItem*)pSC;
|
||||
pIvtr = GetHostPlayer()->GetPack(pSCItem->GetInventory());
|
||||
pItem = pIvtr->GetItem(pSCItem->GetIvtrSlot());
|
||||
if (pItem && pItem->GetCoolTime(&nMax) > 0)
|
||||
{
|
||||
pClock->SetProgressRange(0, nMax);
|
||||
pClock->SetProgressPos(nMax - pItem->GetCoolTime());
|
||||
pClock->SetColor(A3DCOLORRGBA(0, 0, 0, 128));
|
||||
}
|
||||
if (pSCItem->GetInventory() == IVTRTYPE_EQUIPPACK)
|
||||
pCell->SetColor(A3DCOLORRGBA(128, 128, 255, 128));
|
||||
}
|
||||
else if (pSC->GetType() == CECShortcut::SCT_PET)
|
||||
{
|
||||
pSCPet = (CECSCPet*)pSC;
|
||||
CECPetData* pPet = pPetCorral->GetPetData(pSCPet->GetPetIndex());
|
||||
iIconFile = CECGameUIMan::ICONS_INVENTORY;
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
if (pPet)
|
||||
{
|
||||
// dead combat pet
|
||||
if ((pPet->GetClass() == GP_PET_CLASS_COMBAT || pPet->GetClass() == GP_PET_CLASS_EVOLUTION) && pPet->GetHPFactor() == 0.0f)
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}
|
||||
// current active pet
|
||||
else if (pSCPet->IsActivePet())
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pSC->GetType() == CECShortcut::SCT_AUTOFASHION)
|
||||
{
|
||||
iIconFile = CECGameUIMan::ICONS_SUITE;
|
||||
fashionCoolTime = pHost->GetCoolTime(GP_CT_EQUIP_FASHION_ITEM, &fashionCoolTimeMax);
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
if (fashionCoolTimeMax > 0)
|
||||
{
|
||||
pClock->SetProgressRange(0, fashionCoolTimeMax);
|
||||
pClock->SetProgressPos(fashionCoolTimeMax - fashionCoolTime);
|
||||
pClock->SetColor(A3DCOLORRGBA(0, 0, 0, 175));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iIconFile = CECGameUIMan::ICONS_ACTION;
|
||||
if (pSC->GetType() == CECShortcut::SCT_COMMAND)
|
||||
{
|
||||
CECSCCommand* pCommandSC = (CECSCCommand*)pSC;
|
||||
if (GetHostPlayer()->IsInvisible())
|
||||
{
|
||||
if (pCommandSC->GetCommandID() == CECSCCommand::CMD_STARTTRADE ||
|
||||
pCommandSC->GetCommandID() == CECSCCommand::CMD_SELLBOOTH ||
|
||||
pCommandSC->GetCommandID() == CECSCCommand::CMD_BINDBUDDY)
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}
|
||||
else
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
if (pSC->GetCoolTime(&nMax) > 0)
|
||||
{
|
||||
pClock->SetProgressRange(0, nMax);
|
||||
pClock->SetProgressPos(nMax - pSC->GetCoolTime());
|
||||
pClock->SetColor(A3DCOLORRGBA(0, 0, 0, 128));
|
||||
}
|
||||
}*//**/
|
||||
if (pSC != null)
|
||||
{
|
||||
if(pCell.GetDataPtr() == pSC)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
pCell.SetDataPtr(pSC);
|
||||
if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_SKILLGRP)
|
||||
{
|
||||
EC_VIDEO_SETTING setting = EC_Game.GetConfigs().GetVideoSettings();
|
||||
/* pCell.SetCover(GetGameUIMan()->m_pA2DSpriteIcons[CECGameUIMan::ICONS_SKILLGRP],
|
||||
setting.comboSkill[((CECSCSkillGrp)pSC).GetGroupIndex()].nIcon + 1);
|
||||
setting.comboSkill[((CECSCSkillGrp)pSC).GetGroupIndex()].nIcon + 1;*/
|
||||
// fix later now haven't skill group icon yet
|
||||
GetGameUIMan().SetCover(pCell, "unknown", EC_GAMEUI_ICONS.ICONS_SKILL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pSkill != null)
|
||||
{
|
||||
pCell.gameObject.SetActive(true);
|
||||
//BMLogger.Log("HoangDev: QuickBar Set Skill Icon: " + (uint)pSkill.GetSkillID() + " : " + ElementSkill.GetIcon((uint)pSkill.GetSkillID()));
|
||||
var nameskill = ElementSkill.GetIcon((uint)pSkill.GetSkillID());
|
||||
GetGameUIMan().SetCover(pCell, nameskill, EC_GAMEUI_ICONS.ICONS_SKILL);
|
||||
}
|
||||
/* af_GetFileTitle(pSC->GetIconFile(), strFile);
|
||||
strFile.MakeLower();
|
||||
pCell->SetCover(GetGameUIMan()->m_pA2DSpriteIcons[iIconFile],
|
||||
GetGameUIMan()->m_IconMap[iIconFile][strFile]); */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pCell->SetCover(NULL, -1);
|
||||
pCell->SetText(_AL(""));
|
||||
pCell->SetDataPtr(NULL);
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255)); */
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
//public bool UpdateShortcuts()
|
||||
//{
|
||||
// CECShortcut pSC;
|
||||
@@ -230,215 +451,7 @@ namespace BrewMonster
|
||||
// }
|
||||
// return true;
|
||||
//}
|
||||
public bool UpdateShortcuts()
|
||||
{
|
||||
CECShortcut pSC;
|
||||
Image skillImage;
|
||||
CECSCSkill pSCSkill;
|
||||
int iIconFile, nMax;
|
||||
AUIImagePicture pCell;
|
||||
CECSkill pSkill = new CECSkill(-1, -1);
|
||||
AUIClockIcon pClock;
|
||||
|
||||
|
||||
int nCurPanel9 = GetCurPanel1();
|
||||
int nCurPanel8 = GetCurPanel2();
|
||||
|
||||
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
|
||||
if (pHost == null) return false;
|
||||
var a_pSCS = new List<CECShortcutSet>();
|
||||
var a_pszPanel = new List<string>();
|
||||
GetQuickBarNameAndSC(pHost, a_pszPanel, a_pSCS, nCurPanel9, nCurPanel8);
|
||||
|
||||
for (int i = 0; i <= 1/*(int)a_pSCS.Count*/; i++)
|
||||
{
|
||||
if (a_pSCS[i] == null)
|
||||
continue;
|
||||
|
||||
//*//*CDlgQuickBar* pQuickBar = dynamic_cast<CDlgQuickBar*>(GetGameUIMan()->GetDialog(a_pszPanel[i]));
|
||||
//if (!pQuickBar || !pQuickBar->IsShow()) continue;*//*
|
||||
|
||||
for (int j = 0; j < AUIImagePictureList.Count; j++)
|
||||
{
|
||||
pCell = AUIImagePictureList[j];
|
||||
pSC = a_pSCS[i].GetShortcut(j);
|
||||
pClock = pCell.GetClockIcon();
|
||||
pClock.SetProgressRange(0, 1);
|
||||
pClock.SetProgressPos(1);
|
||||
if (pSC != null)
|
||||
{
|
||||
if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_SKILL)
|
||||
{
|
||||
iIconFile = (int)EC_GAMEUI_ICONS.ICONS_SKILL;
|
||||
pSCSkill = (CECSCSkill)pSC;
|
||||
pSkill = pSCSkill.GetSkill();
|
||||
if (false/*m_bDelGoblinSkillSC && GNET::ElementSkill::IsGoblinSkill(pSkill->GetSkillID())*/)
|
||||
{
|
||||
/* a_pSCS[i]->SetShortcut(j, NULL);
|
||||
pSC = NULL;*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pSkill != null && pSkill.ReadyToCast() && pHost.GetPrepSkill() != pSkill)
|
||||
{
|
||||
if (ElementSkill.IsGoblinSkill((uint)pSkill.GetSkillID()))
|
||||
{
|
||||
/* if (pHostGoblin && !pHostGoblin->CheckSkillCastCondition(pSkill))
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pHost.CheckSkillCastCondition(pSkill) == 0)
|
||||
{
|
||||
//pCell.SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
else
|
||||
{
|
||||
//pCell.SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}
|
||||
}
|
||||
}
|
||||
/*else
|
||||
pClock.SetColor(A3DCOLORRGBA(0, 0, 0, 128));*/
|
||||
if (pSkill != null && (pSkill.GetCoolingTime() > 0 ||
|
||||
pHost.GetPrepSkill() == pSkill))
|
||||
{
|
||||
pClock.SetProgressRange(0, pSkill.GetCoolingTime());
|
||||
if (pHost.GetPrepSkill() == pSkill)
|
||||
{
|
||||
pClock.SetProgressPos(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
pClock.SetProgressPos(pSkill.GetCoolingTime() - pSkill.GetCoolingCnt());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*else if (pSC->GetType() == CECShortcut::SCT_ITEM)
|
||||
{
|
||||
iIconFile = CECGameUIMan::ICONS_INVENTORY;
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
pSCItem = (CECSCItem*)pSC;
|
||||
pIvtr = GetHostPlayer()->GetPack(pSCItem->GetInventory());
|
||||
pItem = pIvtr->GetItem(pSCItem->GetIvtrSlot());
|
||||
if (pItem && pItem->GetCoolTime(&nMax) > 0)
|
||||
{
|
||||
pClock->SetProgressRange(0, nMax);
|
||||
pClock->SetProgressPos(nMax - pItem->GetCoolTime());
|
||||
pClock->SetColor(A3DCOLORRGBA(0, 0, 0, 128));
|
||||
}
|
||||
if (pSCItem->GetInventory() == IVTRTYPE_EQUIPPACK)
|
||||
pCell->SetColor(A3DCOLORRGBA(128, 128, 255, 128));
|
||||
}
|
||||
else if (pSC->GetType() == CECShortcut::SCT_PET)
|
||||
{
|
||||
pSCPet = (CECSCPet*)pSC;
|
||||
CECPetData* pPet = pPetCorral->GetPetData(pSCPet->GetPetIndex());
|
||||
iIconFile = CECGameUIMan::ICONS_INVENTORY;
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
if (pPet)
|
||||
{
|
||||
// dead combat pet
|
||||
if ((pPet->GetClass() == GP_PET_CLASS_COMBAT || pPet->GetClass() == GP_PET_CLASS_EVOLUTION) && pPet->GetHPFactor() == 0.0f)
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}
|
||||
// current active pet
|
||||
else if (pSCPet->IsActivePet())
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pSC->GetType() == CECShortcut::SCT_AUTOFASHION)
|
||||
{
|
||||
iIconFile = CECGameUIMan::ICONS_SUITE;
|
||||
fashionCoolTime = pHost->GetCoolTime(GP_CT_EQUIP_FASHION_ITEM, &fashionCoolTimeMax);
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
if (fashionCoolTimeMax > 0)
|
||||
{
|
||||
pClock->SetProgressRange(0, fashionCoolTimeMax);
|
||||
pClock->SetProgressPos(fashionCoolTimeMax - fashionCoolTime);
|
||||
pClock->SetColor(A3DCOLORRGBA(0, 0, 0, 175));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iIconFile = CECGameUIMan::ICONS_ACTION;
|
||||
if (pSC->GetType() == CECShortcut::SCT_COMMAND)
|
||||
{
|
||||
CECSCCommand* pCommandSC = (CECSCCommand*)pSC;
|
||||
if (GetHostPlayer()->IsInvisible())
|
||||
{
|
||||
if (pCommandSC->GetCommandID() == CECSCCommand::CMD_STARTTRADE ||
|
||||
pCommandSC->GetCommandID() == CECSCCommand::CMD_SELLBOOTH ||
|
||||
pCommandSC->GetCommandID() == CECSCCommand::CMD_BINDBUDDY)
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(128, 128, 128));
|
||||
}
|
||||
else
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
if (pSC->GetCoolTime(&nMax) > 0)
|
||||
{
|
||||
pClock->SetProgressRange(0, nMax);
|
||||
pClock->SetProgressPos(nMax - pSC->GetCoolTime());
|
||||
pClock->SetColor(A3DCOLORRGBA(0, 0, 0, 128));
|
||||
}
|
||||
}*//**/
|
||||
if (pSC != null)
|
||||
{
|
||||
if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_SKILLGRP)
|
||||
{
|
||||
EC_VIDEO_SETTING setting = EC_Game.GetConfigs().GetVideoSettings();
|
||||
/* pCell.SetCover(GetGameUIMan()->m_pA2DSpriteIcons[CECGameUIMan::ICONS_SKILLGRP],
|
||||
setting.comboSkill[((CECSCSkillGrp)pSC).GetGroupIndex()].nIcon + 1);
|
||||
setting.comboSkill[((CECSCSkillGrp)pSC).GetGroupIndex()].nIcon + 1;*/
|
||||
// fix later now haven't skill group icon yet
|
||||
GetGameUIMan().SetCover(pCell, "unknown", EC_GAMEUI_ICONS.ICONS_SKILL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pSkill != null)
|
||||
{
|
||||
pCell.gameObject.SetActive(true);
|
||||
//BMLogger.Log("HoangDev: QuickBar Set Skill Icon: " + (uint)pSkill.GetSkillID() + " : " + ElementSkill.GetIcon((uint)pSkill.GetSkillID()));
|
||||
var nameskill = ElementSkill.GetIcon((uint)pSkill.GetSkillID());
|
||||
GetGameUIMan().SetCover(pCell, nameskill, EC_GAMEUI_ICONS.ICONS_SKILL);
|
||||
}
|
||||
/* af_GetFileTitle(pSC->GetIconFile(), strFile);
|
||||
strFile.MakeLower();
|
||||
pCell->SetCover(GetGameUIMan()->m_pA2DSpriteIcons[iIconFile],
|
||||
GetGameUIMan()->m_IconMap[iIconFile][strFile]); */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pCell->SetCover(NULL, -1);
|
||||
pCell->SetText(_AL(""));
|
||||
pCell->SetDataPtr(NULL);
|
||||
pCell->SetColor(A3DCOLORRGB(255, 255, 255)); */
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
private void GetQuickBarNameAndSC(CECHostPlayer pHost, List<string> pszPanel, List<CECShortcutSet> pSCS, int panel9, int panel8)
|
||||
{
|
||||
string dlgName;
|
||||
|
||||
+106
-92
@@ -3,7 +3,6 @@ using BrewMonster.Assets.PerfectWorld.Scripts.Players;
|
||||
using BrewMonster.Managers;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.PerfectWorld.Scripts.Vfx;
|
||||
using BrewMonster.PerfectWorld.Scripts.Vfx;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
@@ -22,6 +21,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using PerfectWorld.Scripts.Managers;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -691,8 +691,6 @@ namespace BrewMonster
|
||||
|
||||
private void OnMsgHstSetCoolTime(ECMSG Msg)
|
||||
{
|
||||
BMLogger.LogError("HoangDev : OnMsgHstSetCoolTime ");
|
||||
|
||||
cmd_set_cooldown pCmd = GPDataTypeHelper.FromBytes<cmd_set_cooldown>((byte[])Msg.dwParam1);
|
||||
|
||||
if (pCmd.cooldown_index < 0)
|
||||
@@ -904,7 +902,7 @@ namespace BrewMonster
|
||||
|
||||
bool bActionStartSkill = false;
|
||||
int iActionTime = 1000;
|
||||
CECPlayerWrapper pWrapper = CECAutoPolicy.GetInstance().GetPlayerWrapper();
|
||||
CECPlayerWrapper pWrapper = CECAutoPolicy.GetInstance().GetPlayerWrapper();
|
||||
|
||||
switch (Convert.ToInt32(Msg.dwParam2))
|
||||
{
|
||||
@@ -968,7 +966,7 @@ namespace BrewMonster
|
||||
bActionStartSkill = true;
|
||||
iActionTime = iWaitTime;
|
||||
Debug.Log($"Cast skill({m_pCurSkill.GetSkillID()})");
|
||||
|
||||
|
||||
// Special logging for return-to-town skill (167)
|
||||
// 回城技能(167)的特殊日志
|
||||
if (m_pCurSkill.GetSkillID() == ID_RETURNTOWN_SKILL)
|
||||
@@ -999,12 +997,9 @@ namespace BrewMonster
|
||||
}
|
||||
case int value2 when value2 == CommandID.HOST_STOP_SKILL:
|
||||
{
|
||||
// Host stop skill
|
||||
// 主角停止技能
|
||||
m_pPrepSkill = null;
|
||||
|
||||
CECSkill
|
||||
pSkillToMatch = m_pCurSkill; // 保存指针值,用在后面函数调用中 | Save pointer value for later function call
|
||||
CECSkill pSkillToMatch = m_pCurSkill;
|
||||
if (m_pCurSkill != null)
|
||||
{
|
||||
ClearComActFlagAllRankNodes(true);
|
||||
@@ -1078,13 +1073,13 @@ namespace BrewMonster
|
||||
|
||||
// Print a notify message
|
||||
// 打印提示消息
|
||||
//EC_Game.GetGameRun().AddFixedMessage(FIXMSG_SKILLINTERRUPT);
|
||||
//EC_Game.GetGameRun().AddFixedMessage(FIXMSG_SKILLINTERRUPT);
|
||||
BMLogger.LogError("Skill interrupted!");
|
||||
|
||||
AP.AP_ActionEvent((int)AP_EVENT. AP_EVENT_STOPSKILL);
|
||||
AP.AP_ActionEvent((int)AP_EVENT.AP_EVENT_STOPSKILL);
|
||||
|
||||
// 通知策略技能被打断 | Notify policy that skill is interrupted
|
||||
CECAutoPolicy.GetInstance().SendEvent_SkillInterrupt(skill_id);
|
||||
CECAutoPolicy.GetInstance().SendEvent_SkillInterrupt(skill_id);
|
||||
break;
|
||||
}
|
||||
case int value2 when value2 == CommandID.OBJECT_CAST_INSTANT_SKILL:
|
||||
@@ -1469,7 +1464,7 @@ namespace BrewMonster
|
||||
CECUIManager.Instance.UpdateSkillRelatedUI();
|
||||
}
|
||||
}
|
||||
public void AssignSkillGrpShortcut( List<SkillGrpShortCutConfig> skillGrpSCConfigArray, CECShortcutSet[] aSCSets)
|
||||
public void AssignSkillGrpShortcut(List<SkillGrpShortCutConfig> skillGrpSCConfigArray, CECShortcutSet[] aSCSets)
|
||||
{
|
||||
for (int i = 0; i < skillGrpSCConfigArray.Count; i++)
|
||||
{
|
||||
@@ -2671,12 +2666,12 @@ namespace BrewMonster
|
||||
// p1 是一个 byte[] 缓冲区;解析为 cmd_notify_hostpos 然后设置位置
|
||||
byte[] buf = (byte[])Msg.dwParam1;
|
||||
cmd_notify_hostpos pCmd = GPDataTypeHelper.FromBytes<cmd_notify_hostpos>(buf);
|
||||
|
||||
|
||||
int idInst = pCmd.tag;
|
||||
Vector3 vPos = new Vector3(pCmd.vPos.x, pCmd.vPos.y, pCmd.vPos.z);
|
||||
int iLine = pCmd.line;
|
||||
|
||||
|
||||
|
||||
|
||||
// Call Goto method to properly handle teleportation
|
||||
// 调用 Goto 方法来正确处理传送
|
||||
if (!Goto(idInst, vPos, iLine))
|
||||
@@ -2685,7 +2680,7 @@ namespace BrewMonster
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return to a target town through skill
|
||||
// 通过技能返回目标城镇
|
||||
// This method implements the Goto logic from the original C++ code
|
||||
@@ -2701,7 +2696,7 @@ namespace BrewMonster
|
||||
// Debug.LogError($"CECHostPlayer::Goto, Failed to jump to instance {idInst}");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
|
||||
// Stop all current work and goto specified position
|
||||
// 停止所有当前工作并转到指定位置
|
||||
if (m_pWorkMan != null)
|
||||
@@ -2710,33 +2705,33 @@ namespace BrewMonster
|
||||
// 如果正在自动移动则停止
|
||||
// Note: IsAutoMoving check would go here if available
|
||||
// 注意:如果可用,IsAutoMoving 检查将放在这里
|
||||
|
||||
|
||||
// Finish all work
|
||||
// 完成所有工作
|
||||
m_pWorkMan.FinishAllWork(true);
|
||||
}
|
||||
|
||||
|
||||
// Add a little height to ensure player's AABB won't embed with building
|
||||
// 增加一点高度以确保玩家的 AABB 不会嵌入建筑物
|
||||
vPos.y += 0.1f;
|
||||
|
||||
|
||||
// Ensure we are not under ground (terrain height check would go here)
|
||||
// 确保我们不会在地下(地形高度检查将放在这里)
|
||||
// Note: Terrain height check is skipped for now as it requires world access
|
||||
// 注意:暂时跳过地形高度检查,因为它需要世界访问
|
||||
|
||||
|
||||
// Set position
|
||||
// 设置位置
|
||||
SetPos(vPos);
|
||||
|
||||
|
||||
// Reset jump state if available
|
||||
// 如果可用则重置跳跃状态
|
||||
// ResetJump(); // Uncomment if ResetJump method exists
|
||||
|
||||
|
||||
// Update camera if available
|
||||
// 如果可用则更新相机
|
||||
// UpdateFollowCamera(false, 10); // Uncomment if UpdateFollowCamera method exists
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3713,10 +3708,10 @@ namespace BrewMonster
|
||||
return false;
|
||||
|
||||
if (!bCombo)
|
||||
//ClearComboSkill();
|
||||
ClearComboSkill();
|
||||
|
||||
if (idSelTarget == 0)
|
||||
idSelTarget = m_idSelTarget;
|
||||
if (idSelTarget == 0)
|
||||
idSelTarget = m_idSelTarget;
|
||||
|
||||
CECSkill pSkill = GetPositiveSkillByID(idSkill);
|
||||
if (pSkill == null) pSkill = GetEquipSkillByID(idSkill);
|
||||
@@ -3736,12 +3731,12 @@ namespace BrewMonster
|
||||
return true;
|
||||
}
|
||||
|
||||
//int iCon = CheckSkillCastCondition(pSkill);
|
||||
//if (iCon)
|
||||
//{
|
||||
// ProcessSkillCondition(iCon);
|
||||
// return false;
|
||||
//}
|
||||
int iCon = CheckSkillCastCondition(pSkill);
|
||||
if (iCon != 0)
|
||||
{
|
||||
ProcessSkillCondition(iCon);
|
||||
return false;
|
||||
}
|
||||
|
||||
//// Get force attack flag
|
||||
bool bForceAttack = false;
|
||||
@@ -3751,20 +3746,20 @@ namespace BrewMonster
|
||||
bForceAttack = iForceAtk > 0 ? true : false;
|
||||
|
||||
//// Check negative effect skill
|
||||
//if (pSkill.GetType() == (int)skill_type.TYPE_ATTACK || pSkill.GetType() == (int)skill_type.TYPE_CURSE)
|
||||
//{
|
||||
// if (idSelTarget == m_PlayerInfo.cid)
|
||||
// {
|
||||
// // Host cannot spell negative effect magic to himself.
|
||||
// EC_Game.GetGameRun().AddFixedChannelMsg(FIXMSG_TARGETWRONG, GP_CHAT_FIGHT);
|
||||
// return false;
|
||||
// }
|
||||
// else if (idSelTarget != 0)
|
||||
// {
|
||||
// if (AttackableJudge(idSelTarget, bForceAttack) != 1)
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
if (pSkill.GetType() == (int)skill_type.TYPE_ATTACK || pSkill.GetType() == (int)skill_type.TYPE_CURSE)
|
||||
{
|
||||
if (idSelTarget == m_PlayerInfo.cid)
|
||||
{
|
||||
// Host cannot spell negative effect magic to himself.
|
||||
//EC_Game.GetGameRun().AddFixedChannelMsg(FIXMSG_TARGETWRONG, GP_CHAT_FIGHT);
|
||||
return false;
|
||||
}
|
||||
else if (idSelTarget != 0)
|
||||
{
|
||||
if (AttackableJudge(idSelTarget, bForceAttack) != 1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//// Check whether target type match
|
||||
int idCastTarget = idSelTarget;
|
||||
@@ -3876,7 +3871,7 @@ namespace BrewMonster
|
||||
else if (iTargetType == 2)
|
||||
iAliveFlag = 2;
|
||||
|
||||
/*CECObject pObject = EC_Game.GetGameRun().GetWorld().GetObject(idCastTarget, iAliveFlag);
|
||||
/* CECObject pObject = EC_Game.GetGameRun().GetWorld().GetObject(idCastTarget, iAliveFlag);
|
||||
if (!pObject)
|
||||
return false;*/
|
||||
}
|
||||
@@ -3890,8 +3885,8 @@ namespace BrewMonster
|
||||
|
||||
if (!pSkill.IsInstant() && pSkill.GetType() != (int)Skilltype.TYPE_FLASHMOVE)
|
||||
{
|
||||
/* if (!NaturallyStopMoving())
|
||||
return false; */ // Couldn't stop naturally, so cancel casting skill
|
||||
if (!NaturallyStopMoving())
|
||||
return false; // Couldn't stop naturally, so cancel casting skill
|
||||
}
|
||||
else if (pSkill.GetType() == (int)Skilltype.TYPE_FLASHMOVE)
|
||||
{
|
||||
@@ -3925,9 +3920,9 @@ namespace BrewMonster
|
||||
{
|
||||
bool bTraceOK = false;
|
||||
bool bUseAutoPF = false;
|
||||
/* CECPlayerWrapper pWrapper = CECAutoPolicy::GetInstance().GetPlayerWrapper();
|
||||
if (CECAutoPolicy::GetInstance().IsAutoPolicyEnabled() && pWrapper.GetAttackError() >= 2)
|
||||
bUseAutoPF = true;*/
|
||||
CECPlayerWrapper pWrapper = CECAutoPolicy.GetInstance().GetPlayerWrapper();
|
||||
if (CECAutoPolicy.GetInstance().IsAutoPolicyEnabled() && pWrapper.GetAttackError() >= 2)
|
||||
bUseAutoPF = true;
|
||||
|
||||
if (idCastTarget == 0)
|
||||
{
|
||||
@@ -4134,7 +4129,24 @@ namespace BrewMonster
|
||||
Buffer.BlockCopy(buffer, index + contentBytes, tail, 0, trailing);
|
||||
}
|
||||
}
|
||||
public bool NaturallyStopMoving()
|
||||
{
|
||||
// if (!m_MoveCtrl.IsStop())
|
||||
if (!IsPlayerMoving())
|
||||
return true; // Host has been stopped
|
||||
|
||||
if (m_iMoveMode == (int)MoveMode.MOVE_FREEFALL || InSlidingState() || IsJumping())
|
||||
return false; // Host couldn't stop naturally
|
||||
|
||||
if (!m_pWorkMan.IsStanding())
|
||||
{
|
||||
m_pWorkMan.FinishAllWork(true);
|
||||
}
|
||||
|
||||
m_MoveCtrl.SendStopMoveCmd();
|
||||
|
||||
return true;
|
||||
}
|
||||
public bool CastSkill(int idTarget, bool bForceAttack, CECObject pTarget = null)
|
||||
{
|
||||
// Check if prep skill is valid, ready to cast, and not currently spelling magic
|
||||
@@ -4393,7 +4405,7 @@ namespace BrewMonster
|
||||
// 通过技能返回目标城镇
|
||||
private bool ReturnToTargetTown(int idTarget, bool bCombo = false)
|
||||
{
|
||||
|
||||
|
||||
if (!CanDo(ActionCanDo.CANDO_SPELLMAGIC))
|
||||
{
|
||||
return false;
|
||||
@@ -4407,7 +4419,7 @@ namespace BrewMonster
|
||||
Debug.LogError("ReturnToTargetTown: Skill 167 not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!bCombo)
|
||||
{
|
||||
@@ -4443,7 +4455,7 @@ namespace BrewMonster
|
||||
|
||||
m_pPrepSkill = pSkill;
|
||||
byte byPVPMask = glb_BuildPVPMask(false);
|
||||
|
||||
|
||||
// Call c2s_CmdCastSkill with target parameter
|
||||
// 使用目标参数调用 c2s_CmdCastSkill
|
||||
int targets = 1;
|
||||
@@ -4667,19 +4679,13 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
|
||||
// Check combo skill prerequisite (for night shadow continuous skills)
|
||||
// Note: GetComboSkPreSkill and IsActiveComboSkill methods need to be implemented
|
||||
// TODO: Implement GetComboSkPreSkill in ElementSkill/CECSkill
|
||||
// TODO: Implement IsActiveComboSkill in CECComboSkillState
|
||||
/*
|
||||
if (pSkill.GetComboSkPreSkill() != 0)
|
||||
{
|
||||
if (!CECComboSkillState.Instance.IsActiveComboSkill(pSkill.GetSkillID()))
|
||||
if (!CECComboSkillState.Instance.IsActiveComboSkill((uint)pSkill.GetSkillID()))
|
||||
{
|
||||
return 13; // Combo skill not active
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Build UseRequirement info
|
||||
UseRequirement Info = new UseRequirement();
|
||||
@@ -4691,35 +4697,24 @@ namespace BrewMonster
|
||||
Info.is_combat = IsFighting();
|
||||
Info.hp = m_BasicProps.iCurHP;
|
||||
Info.max_hp = m_ExtProps.bs.max_hp;
|
||||
// Info.combo_state = CECComboSkillState.Instance.GetComboSkillState(); // TODO: Implement GetComboSkillState
|
||||
Info.combo_state = CECComboSkillState.Instance.GetComboSkillState(); // TODO: Implement GetComboSkillState
|
||||
|
||||
// Get weapon's major class ID
|
||||
// Equipment inventory slot constants from CECPlayer.IndexOfIteminEquipmentInventory
|
||||
const int EQUIPIVTR_WEAPON = 0; // Weapon slot
|
||||
const int EQUIPIVTR_PROJECTILE = 4; // Projectile slot (arrows)
|
||||
// Get weapon's major class ID
|
||||
int iReason = 0;
|
||||
|
||||
Info.weapon = 0;
|
||||
Info.arrow = 0;
|
||||
|
||||
EC_IvtrItem pWeaponItem = m_pEquipPack.GetItem(EQUIPIVTR_WEAPON);
|
||||
if (pWeaponItem != null && pWeaponItem is EC_IvtrEquip pWeaponEquip)
|
||||
{
|
||||
// Check if weapon has endurance
|
||||
if (pWeaponEquip.CurEndurance > 0)
|
||||
{
|
||||
// TODO: Implement CanUseEquipment method to check level/class requirements
|
||||
// For now, use the template ID as weapon type
|
||||
Info.weapon = pWeaponItem.GetTemplateID();
|
||||
}
|
||||
}
|
||||
CECIvtrWeapon pWeapon = (CECIvtrWeapon)m_pEquipPack.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WEAPON);
|
||||
if (pWeapon == null || pWeapon.GetCurEndurance() == 0)
|
||||
Info.weapon = 0;
|
||||
else if (!CanUseEquipment(pWeapon, ref iReason))
|
||||
Info.weapon = (iReason == 5) ? (int)pWeapon.GetDBMajorType().id : 0;
|
||||
else
|
||||
Info.weapon = (int)pWeapon.GetDBMajorType().id;
|
||||
|
||||
// Get remaining arrow number
|
||||
EC_IvtrItem pArrowItem = m_pEquipPack.GetItem(EQUIPIVTR_PROJECTILE);
|
||||
if (pArrowItem != null)
|
||||
CECIvtrArrow pArrow = (CECIvtrArrow)m_pEquipPack.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_PROJECTILE);
|
||||
if (pArrow != null && CanUseProjectile(pArrow))
|
||||
{
|
||||
// TODO: Implement CanUseProjectile method to check requirements
|
||||
// For now, use the item count
|
||||
Info.arrow = pArrowItem.GetCount();
|
||||
Info.arrow = pArrow.GetCount();
|
||||
}
|
||||
|
||||
// Call ElementSkill Condition check
|
||||
@@ -4760,7 +4755,26 @@ namespace BrewMonster
|
||||
|
||||
return iMsg >= 0;
|
||||
}
|
||||
public bool CanUseProjectile(CECIvtrArrow pArrow)
|
||||
{
|
||||
if (pArrow == null)
|
||||
return false;
|
||||
|
||||
CECIvtrWeapon pWeapon = (CECIvtrWeapon)m_pEquipPack.GetItem((int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WEAPON);
|
||||
if (pWeapon == null)
|
||||
return false;
|
||||
|
||||
IVTR_ESSENCE_WEAPON we = pWeapon.GetEssence();
|
||||
if (we.weapon_type != (int)WeaponType.WEAPONTYPE_RANGE)
|
||||
return false;
|
||||
|
||||
IVTR_ESSENCE_ARROW ae = pArrow.GetEssence();
|
||||
if (we.require_projectile != (int)pArrow.GetDBSubType().id ||
|
||||
we.weapon_level < ae.iWeaponReqLow || we.weapon_level > ae.iWeaponReqHigh)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool CanSelectTarget(int idTarget)
|
||||
{
|
||||
if (idTarget == 0 || idTarget == this.GetCharacterID())
|
||||
@@ -6885,7 +6899,7 @@ namespace BrewMonster
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClearComboSkill()
|
||||
public void ClearComboSkill()
|
||||
{
|
||||
if (m_pComboSkill != null)
|
||||
{
|
||||
@@ -7166,7 +7180,7 @@ namespace BrewMonster
|
||||
for (i = 0; i < m_pEquipPack.GetSize(); i++)
|
||||
{
|
||||
var pItem = m_pEquipPack.GetItem(i);
|
||||
if(pItem == null)
|
||||
if (pItem == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -7277,11 +7291,11 @@ namespace BrewMonster
|
||||
return m_pPetCorral;
|
||||
}
|
||||
|
||||
public bool IsPlayerMoving()
|
||||
public bool IsPlayerMoving()
|
||||
{
|
||||
return m_pWorkMan.IsMoving();
|
||||
}
|
||||
public CECComboSkill GetComboSkill() { return m_pComboSkill; }
|
||||
public CECComboSkill GetComboSkill() { return m_pComboSkill; }
|
||||
|
||||
}
|
||||
public struct SkillShortCutConfig
|
||||
|
||||
+9
-356
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user