convert for test done

This commit is contained in:
VDH
2025-11-07 18:01:47 +07:00
parent 38a2c0d46f
commit d4af8e33f9
28 changed files with 1512 additions and 1043 deletions
@@ -1,162 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using ModelRenderer.Scripts.Common;
namespace BrewMonster.Common
{
public class CECStringTab
{
private Dictionary<int, string> m_AStrTab = new Dictionary<int, string>();
private Dictionary<int, string> m_WStrTab = new Dictionary<int, string>();
protected bool m_bInit = false;
protected bool m_bUnicode = false;
public bool IsInitialized() => m_bInit;
public void Clear()
{
m_AStrTab.Clear();
m_WStrTab.Clear();
m_bInit = false;
m_bUnicode = false;
}
public bool Init(string szFile, bool bUnicode)
{
bool bRet = false;
if (bUnicode)
{
bRet = LoadWideStrings(szFile);
}
if (!bRet)
{
BMLogger.LogError($"EC_StringTab::Init: {szFile} File load failed");
return false;
}
m_bInit = true;
return true;
}
private bool LoadWideStrings(string szFile)
{
AWScriptFile ScriptFile = new AWScriptFile();
if (!ScriptFile.Open(szFile)) return false;
bool bIndex = false;
bool bFileEnd = true;
// Read configs
while (ScriptFile.GetNextToken(true))
{
string tokenStr = ByteToStringUtils.UshortArrayToUnicodeString(ScriptFile.m_szToken);
if (ByteToStringUtils.UshortArrayToUnicodeString(ScriptFile.m_szToken).StartsWith("#_index"))
bIndex = true;
else if (ByteToStringUtils.UshortArrayToUnicodeString(ScriptFile.m_szToken).StartsWith("#_begin"))
{
bFileEnd = false;
break;
}
}
if (bFileEnd)
{
ScriptFile.Close();
return true;
}
if (bIndex)
{
// Every string has a preset index
while (ScriptFile.PeekNextToken(true))
{
int n = ScriptFile.GetNextTokenAsInt(true);
ScriptFile.GetNextToken(false);
string pstr = ByteToStringUtils.UshortArrayToUnicodeString(ScriptFile.m_szToken);
if (string.IsNullOrEmpty(pstr))
{
ScriptFile.Close();
BMLogger.LogWarning($"EC_StringTab::LoadWideStrings: {szFile} Not enough memory");
return false;
}
if (!m_WStrTab.TryAdd(n, pstr))
{
BMLogger.LogWarning($"EC_StringTab::LoadWideStrings: {szFile} Failed to add string to dictionary");
return false;
}
}
}
else
{
int iCnt = 0;
// Read strings sequently
while (ScriptFile.GetNextToken(true))
{
string pstr = ByteToStringUtils.UshortArrayToUnicodeString(ScriptFile.m_szToken);
if (string.IsNullOrEmpty(pstr))
{
ScriptFile.Close();
BMLogger.LogError($"EC_StringTab::LoadWideStrings: {szFile} Not enough memory");
return false;
}
if (!m_WStrTab.TryAdd(iCnt++, pstr))
{
BMLogger.LogError($"EC_StringTab::LoadWideStrings: {szFile} Failed to add string to dictionary");
return false;
}
}
}
ScriptFile.Close();
return true;
}
/// <summary>
/// Get a string by index from the Unicode string table
/// </summary>
/// <param name="index">The index of the string to retrieve</param>
/// <returns>The string at the given index, or null if not found</returns>
public string GetString(int index)
{
if (m_WStrTab.TryGetValue(index, out string result))
{
return result;
}
return null;
}
/// <summary>
/// Get a string by index from the ANSI string table
/// </summary>
/// <param name="index">The index of the string to retrieve</param>
/// <returns>The string at the given index, or null if not found</returns>
public string GetANSIString(int index)
{
if (m_AStrTab.TryGetValue(index, out string result))
{
return result;
}
return null;
}
/// <summary>
/// Check if a string exists at the given index
/// </summary>
/// <param name="index">The index to check</param>
/// <returns>True if a string exists at the index</returns>
public bool HasString(int index)
{
return m_WStrTab.ContainsKey(index) || m_AStrTab.ContainsKey(index);
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: b95d268e43a5da14f9cf8e1ed98bfd73
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BrewMonster.Assets.PerfectWorld.Scripts.Common
{
public abstract class Singleton<T> where T : class, new()
{
private static readonly Lazy<T> _instance = new Lazy<T>(() => new T());
public static T Instance => _instance.Value;
// Protected constructor prevents external instantiation
protected Singleton()
{
if (_instance.IsValueCreated)
throw new InvalidOperationException($"Singleton<{typeof(T).Name}> already created!");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6034e7cce8bae674c88f8d3de26c06aa
@@ -14,7 +14,7 @@ namespace BrewMonster.Common
{
m_pStringTab = new CECStringTab();
}
m_pStringTab.Clear();
//m_pStringTab.Clear();
string path = Path.Combine(Application.streamingAssetsPath, "configs/skillstr.txt");
m_pStringTab.Init(path, true);
}
@@ -27,7 +27,7 @@ namespace BrewMonster.Common
m_pStringTab = new CECStringTab();
}
m_pStringTab.Clear();
//m_pStringTab.Clear();
string path = Path.Combine(Application.streamingAssetsPath, "configs/item_desc.txt");
m_pStringTab.Init(path, true);
}
@@ -40,7 +40,7 @@ namespace BrewMonster.Common
m_pStringTab = new CECStringTab();
}
m_pStringTab.Clear();
//m_pStringTab.Clear();
string path = Path.Combine(Application.streamingAssetsPath, "configs/fixed_msg.txt");
m_pStringTab.Init(path, true);
}
@@ -1,7 +1,7 @@
using ModelRenderer.Scripts.GameData;
using BrewMonster.Scripts.Task;
using UnityEngine;
using BrewMonster.Common;
using BrewMonster;
using System.Collections.Generic;
using System.IO;
namespace BrewMonster.Network
@@ -13,11 +13,11 @@ namespace BrewMonster.Network
private static elementdataman m_pElementDataMan; // global element templates manager
private static CECGameRun m_pGameRun; // Game running object
private static BrewMonster.Common.CECStringTab m_FixedMsgs; // Fixed message table
private static BrewMonster.Common.CECStringTab m_ItemDesc; // Item desciption string table
private static BrewMonster.Common.CECStringTab m_ItemExtDesc; // Item extend description string table
private static BrewMonster.Common.CECStringTab m_SkillDesc; // Skill description string table
private static BrewMonster.Common.CECStringTab m_BuffDesc; // Buff description string table
private static BrewMonster.CECStringTab m_FixedMsgs; // Fixed message table
private static BrewMonster.CECStringTab m_ItemDesc; // Item desciption string table
private static BrewMonster.CECStringTab m_ItemExtDesc; // Item extend description string table
private static BrewMonster.CECStringTab m_SkillDesc; // Skill description string table
private static BrewMonster.CECStringTab m_BuffDesc; // Buff description string table
private static Dictionary<int, ItemMsgMapEntry> m_ItemMsgMap; // TemplateId -> (MessageId, DisplayMode)
#endregion
@@ -26,11 +26,11 @@ namespace BrewMonster.Network
public static elementdataman GetElementDataMan() { return m_pElementDataMan; }
// String table getters
public static BrewMonster.Common.CECStringTab GetFixedMsgs() { return m_FixedMsgs; }
public static BrewMonster.Common.CECStringTab GetItemDesc() { return m_ItemDesc; }
public static BrewMonster.Common.CECStringTab GetItemExtDesc() { return m_ItemExtDesc; }
public static BrewMonster.Common.CECStringTab GetSkillDesc() { return m_SkillDesc; }
public static BrewMonster.Common.CECStringTab GetBuffDesc() { return m_BuffDesc; }
public static BrewMonster.CECStringTab GetFixedMsgs() { return m_FixedMsgs; }
public static BrewMonster.CECStringTab GetItemDesc() { return m_ItemDesc; }
public static BrewMonster.CECStringTab GetItemExtDesc() { return m_ItemExtDesc; }
public static BrewMonster.CECStringTab GetSkillDesc() { return m_SkillDesc; }
public static BrewMonster.CECStringTab GetBuffDesc() { return m_BuffDesc; }
public static bool TryGetItemMsg(int templateId, out int messageId, out int displayMode)
{
messageId = 0;
@@ -78,11 +78,11 @@ namespace BrewMonster.Network
private static void InitializeStringTables()
{
// Initialize string table instances
m_FixedMsgs = new BrewMonster.Common.CECStringTab();
m_ItemDesc = new BrewMonster.Common.CECStringTab();
m_ItemExtDesc = new BrewMonster.Common.CECStringTab();
m_SkillDesc = new BrewMonster.Common.CECStringTab();
m_BuffDesc = new BrewMonster.Common.CECStringTab();
m_FixedMsgs = new BrewMonster.CECStringTab();
m_ItemDesc = new BrewMonster.CECStringTab();
m_ItemExtDesc = new BrewMonster.CECStringTab();
m_SkillDesc = new BrewMonster.CECStringTab();
m_BuffDesc = new BrewMonster.CECStringTab();
// Load string files from StreamingAssets/configs directory
string dataPath = Application.streamingAssetsPath + "/configs/";
@@ -563,7 +563,7 @@ namespace BrewMonster.Scripts.Managers
var itemDesc = EC_Game.GetItemDesc();
if (itemDesc != null && itemDesc.IsInitialized())
{
string description = itemDesc.GetString(messageId);
string description = itemDesc.GetWideString(messageId);
if (!string.IsNullOrEmpty(description))
{
return description;
@@ -576,7 +576,7 @@ namespace BrewMonster.Scripts.Managers
var itemDesc = EC_Game.GetItemDesc();
if (itemDesc != null && itemDesc.IsInitialized())
{
string description = itemDesc.GetString(templateId);
string description = itemDesc.GetWideString(templateId);
if (!string.IsNullOrEmpty(description))
return description;
}
@@ -605,7 +605,7 @@ namespace BrewMonster.Scripts.Managers
var itemExtDesc = EC_Game.GetItemExtDesc();
if (itemExtDesc != null && itemExtDesc.IsInitialized())
{
string extendedDesc = itemExtDesc.GetString(messageId);
string extendedDesc = itemExtDesc.GetWideString(messageId);
if (!string.IsNullOrEmpty(extendedDesc))
{
return extendedDesc;
@@ -618,7 +618,7 @@ namespace BrewMonster.Scripts.Managers
var itemExtDesc = EC_Game.GetItemExtDesc();
if (itemExtDesc != null && itemExtDesc.IsInitialized())
{
string extendedDesc = itemExtDesc.GetString(templateId);
string extendedDesc = itemExtDesc.GetWideString(templateId);
if (!string.IsNullOrEmpty(extendedDesc))
return extendedDesc;
}
@@ -1381,7 +1381,7 @@ namespace PerfectWorld.Scripts.Managers
var tab = EC_Game.GetItemDesc();
if (tab != null && tab.IsInitialized())
{
string s = tab.GetString(id);
string s = tab.GetWideString(id);
if (!string.IsNullOrEmpty(s)) return s;
}
// Fallback labels for common IDs when the table lacks #_index entries
@@ -1,6 +1,508 @@
using BrewMonster.Network;
using BrewMonster.Scripts.Skills;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
public class CECSkill
namespace BrewMonster
{
}
// CECSkillStr class - inherits from SkillStr
public class CECSkillStr : SkillStr
{
public override string Find(int id)
{
// TODO: Implement GetNameDisplay - requires game instance access
BrewMonster.CECStringTab pStrTab = EC_Game.GetSkillDesc();
string str = pStrTab.GetWideString(id);
return string.IsNullOrEmpty(str) ? str : string.Empty;
}
}
// Skill array wrapper structure
public struct SkillArrayWrapper
{
public Dictionary<uint, int> Array;
public SkillArrayWrapper(Dictionary<uint, int> rhs)
{
Array = rhs ?? new Dictionary<uint, int>();
RemoveInvalid();
}
// Remove invalid skills (for example, those with ID == 0)
public void RemoveInvalid()
{
if (Array == null) return;
var invalidKeys = Array
.Where(kv => kv.Key == 0)
.Select(kv => kv.Key)
.ToList();
foreach (var key in invalidKeys)
Array.Remove(key);
}
public bool Find(uint id)
{
return Array != null && Array.ContainsKey(id);
}
public bool Empty()
{
return Array == null || Array.Count == 0;
}
public int Count()
{
return Array?.Count ?? 0;
}
public uint this[int index]
{
get
{
if (Array == null || index < 0 || index >= Array.Count)
throw new System.IndexOutOfRangeException();
return Array.ElementAt(index).Key;
}
}
}
// CECSkill class
public class CECSkill
{
// Skill type enum
public enum SkillType
{
TYPE_ATTACK = 1, // 攻击技能
TYPE_BLESS, // 祝福技能
TYPE_CURSE, // 诅咒技能
TYPE_SUMMON, // 召唤
TYPE_PASSIVE, // 被动
TYPE_ENABLED, // 启用
TYPE_LIVE, // 生活
TYPE_FLASHMOVE, // 瞬移
TYPE_PRODUCE, // 生产
TYPE_BLESSPET, // 宠物祝福
TYPE_NEUTRALBLESS, // 中立祝福
}
// Range type enum
public enum RangeType
{
RANGE_POINT = 0, // 点
RANGE_LINE, // 线
RANGE_SELFSPHERE, // 自身为圆心的圆
RANGE_TARGETSPHERE, // 目标为圆心的圆
RANGE_TAPER, // 圆锥
RANGE_SLEF, // 自身
}
// Attributes
private ElementSkill m_pSkillCore;
private int m_idSkill; // Skill ID
private int m_iLevel; // Skill level
private int m_iCoolCnt; // Cooling time counter
private int m_iCoolTime; // Total cooling time
private bool m_bCooling; // In cooling state
private int m_iChargeCnt; // Charging time counter
private int m_iChargeMax; // Charging time maximum count value
private bool m_bCharging; // In charging state
// Static skill string provider
private static CECSkillStr l_SkillStr = new CECSkillStr();
// Constructor
public CECSkill(int id, int iLevel)
{
m_pSkillCore = ElementSkill.Create((uint)id, iLevel);
if (m_pSkillCore == null)
{
// Fallback to default skill
m_pSkillCore = ElementSkill.Create(1, 1);
}
m_idSkill = id;
m_iLevel = iLevel;
m_iCoolTime = m_pSkillCore != null ? m_pSkillCore.GetCoolingTime() : 0;
m_iCoolCnt = 0;
m_bCooling = false;
m_iChargeCnt = 0;
m_iChargeMax = 0;
m_bCharging = false;
}
// Tick routine
public void Tick(float deltaTime)
{
// Convert deltaTime (seconds) to milliseconds
int tickTime = (int)(deltaTime * 1000f);
if (m_bCooling)
{
// In cooling state
m_iCoolCnt -= tickTime;
if (m_iCoolCnt <= 0)
{
m_iCoolCnt = 0;
m_bCooling = false;
// TODO: do something here ?
}
}
if (m_bCharging)
{
// In charging state
m_iChargeCnt += tickTime;
if (m_iChargeCnt >= m_iChargeMax)
{
m_iChargeCnt = m_iChargeMax;
m_bCharging = false;
// TODO: do something here ?
}
}
}
// Skill level up
public void LevelUp()
{
m_iLevel++;
if (m_pSkillCore != null)
{
m_pSkillCore.SetLevel(m_iLevel);
}
}
// Set Skill level
public void SetLevel(int iLevel)
{
m_iLevel = iLevel;
if (m_pSkillCore != null)
{
m_pSkillCore.SetLevel(m_iLevel);
}
}
// Start into cooling state
// iTotalTime: total cooling time, 0 means to use cooling time in database
public void StartCooling(int iTotalTime, int iStartCnt)
{
m_iCoolTime = iTotalTime != 0 ? iTotalTime : GetCoreCoolingTime();
m_iCoolCnt = iStartCnt;
m_bCooling = true;
}
// Ready to be cast ?
public bool ReadyToCast()
{
return !m_bCooling;
}
// Get cooling time counter
public int GetCoolingCnt()
{
return m_iCoolCnt;
}
// Get total cooling time
public int GetCoolingTime()
{
return m_iCoolTime;
}
// Start charging
public void StartCharging(int iChargeMax)
{
if (m_pSkillCore != null && m_pSkillCore.IsWarmup())
{
m_iChargeMax = iChargeMax;
m_iChargeCnt = 0;
m_bCharging = true;
}
}
// End charging
public void EndCharging()
{
m_bCharging = false;
}
// Get charging flag
public bool IsCharging()
{
return m_bCharging;
}
// Get charging counter
public int GetChargingCnt()
{
return m_iChargeCnt;
}
// Get charging maximum count
public int GetChargingMax()
{
return m_iChargeMax;
}
// Charge full
public bool ChargeFull()
{
return m_pSkillCore != null && m_pSkillCore.IsWarmup() && m_iChargeCnt >= m_iChargeMax;
}
// Get skill ID
public int GetSkillID()
{
return m_idSkill;
}
// Get skill level
public int GetSkillLevel()
{
return m_iLevel;
}
// Get skill icon file
public string GetIconFile()
{
return m_pSkillCore != null ? m_pSkillCore.GetIcon() ?? string.Empty : string.Empty;
}
public string GetName()
{
return m_pSkillCore != null ? m_pSkillCore.GetName() ?? string.Empty : string.Empty;
}
public string GetNameDisplay()
{
// TODO: Implement GetNameDisplay - requires game instance access
// return g_pGame->GetSkillDesc()->GetWideString(GetSkillID() * 10);
return string.Empty;
}
public string GetDesc()
{
if (m_pSkillCore == null || l_SkillStr == null)
return string.Empty;
StringBuilder sb = new StringBuilder(1024);
var skillStr = l_SkillStr as SkillStr;
string result = m_pSkillCore.GetIntroduction(sb, 1024, skillStr);
return result;
}
public int GetCoreCoolingTime()
{
return m_pSkillCore != null ? m_pSkillCore.GetCoolingTime() : 0;
}
public int GetExecuteTime()
{
return m_pSkillCore != null ? m_pSkillCore.GetExecuteTime() : 0;
}
public int GetType()
{
return m_pSkillCore != null ? m_pSkillCore.GetType() : 0;
}
public int GetRangeType()
{
return m_pSkillCore != null ? m_pSkillCore.GetRangeType() : 0;
}
public float GetCastRange(float fAtkDist, float fPrayDistancePlus)
{
return m_pSkillCore != null ? m_pSkillCore.GetPrayRange(fAtkDist, fPrayDistancePlus) : 0f;
}
/* public string GetEffect()
{
return m_pSkillCore != null ? m_pSkillCore.GetEffect() ?? string.Empty : string.Empty;
}
*/
public int GetTargetType()
{
return m_pSkillCore != null ? m_pSkillCore.GetTargetType() : 0;
}
public int GetCastEnv()
{
return m_pSkillCore != null ? m_pSkillCore.GetCastEnv() : 0;
}
/* public int[] GetRequiredGenius()
{
return m_pSkillCore != null ? m_pSkillCore.GetRequiredGenius(m_idSkill) ?? new int[0] : new int[0];
}*/
public int GetShowOrder()
{
return m_pSkillCore != null ? m_pSkillCore.GetShowOrder() : 0;
}
public bool IsChargeable()
{
return m_pSkillCore != null && m_pSkillCore.IsWarmup();
}
/* public string GetNativeName()
{
return m_pSkillCore != null ? m_pSkillCore.GetNativeName() ?? string.Empty : string.Empty;
}*/
public bool ValidWeapon(int idWeapon)
{
return m_pSkillCore != null && m_pSkillCore.ValidWeapon(idWeapon);
}
/*
public bool ValidShape(int iShape)
{
return m_pSkillCore != null && m_pSkillCore.IsValidForm((char)iShape);
}*/
public bool ChangeToMelee()
{
return m_pSkillCore != null && m_pSkillCore.IsAutoAttack();
}
public bool IsInstant()
{
return m_pSkillCore != null && m_pSkillCore.IsInstant();
}
public bool IsDurative()
{
return m_pSkillCore != null && m_pSkillCore.IsDurative();
}
public SkillArrayWrapper GetJunior()
{
var juniorList = m_pSkillCore.GetJunior();
return new SkillArrayWrapper(juniorList);
}
public int GetRequiredLevel()
{
return m_pSkillCore != null ? m_pSkillCore.GetRequiredLevel() : 0;
}
public int GetRequiredSp()
{
return m_pSkillCore != null ? m_pSkillCore.GetRequiredSp() : 0;
}
public int GetRequiredBook()
{
return m_pSkillCore != null ? m_pSkillCore.GetRequiredBook() : 0;
}
/* public SkillArrayWrapper GetRequiredSkill()
{
return m_pSkillCore != null ? m_pSkillCore.GetRequiredSkill() : new SkillArrayWrapper(new List<SkillArrayWrapper.IDLevelPair>());
}*/
public int GetRequiredMoney()
{
return m_pSkillCore != null ? m_pSkillCore.GetRequiredMoney() : 0;
}
/* public int GetRequiredItem()
{
return m_pSkillCore != null ? m_pSkillCore.GetItemCost() : 0;
}*/
// 获取技能公共冷却mask,其中bit0-4为技能公共冷却,bit5-9为物品公共冷却
/* public int GetCommonCoolDown()
{
return m_pSkillCore != null ? m_pSkillCore.GetCommonCoolDown() : 0;
}*/
// 获取技能公共冷却时间,单位毫秒
/* public int GetCommonCoolDownTime()
{
return m_pSkillCore != null ? m_pSkillCore.GetCommonCoolDownTime() : 0;
}*/
// Get skill description (static method)
public static bool GetDesc(int idSkill, int iLevel, out string szText, int iBufLen)
{
szText = string.Empty;
if (iBufLen <= 0)
{
return false;
}
CECSkill pSkill = new CECSkill(idSkill, iLevel);
if (pSkill == null)
{
return false;
}
string sz = pSkill.GetDesc();
if (sz != null && sz.Length < iBufLen)
{
szText = sz;
return true;
}
return false;
}
// Check skill type
public bool IsGoblinSkill()
{
return m_pSkillCore != null && m_pSkillCore.GetCls() == 258;
}
public bool IsPlayerSkill()
{
// TODO: NUM_PROFESSION should be defined elsewhere
const int NUM_PROFESSION = 256; // Placeholder
int cls = m_pSkillCore != null ? m_pSkillCore.GetCls() : -1;
return cls >= 0 && cls < NUM_PROFESSION;
}
public bool IsGeneralSkill()
{
return GetCls() == 255;
}
public int GetCls()
{
return m_pSkillCore != null ? m_pSkillCore.GetCls() : -1;
}
// 技能等级
public int GetRank()
{
return m_pSkillCore != null ? m_pSkillCore.GetRank() : 0;
}
// 技能最大等级
public int GetMaxLevel()
{
return m_pSkillCore != null ? m_pSkillCore.GetMaxLevel() : 0;
}
/* public int GetComboSkPreSkill()
{
return m_pSkillCore != null ? m_pSkillCore.GetComboSkPreSkill() : 0;
}*/
public bool IsPositiveSkill()
{
int t = GetType();
return t != (int)SkillType.TYPE_PASSIVE
&& t != (int)SkillType.TYPE_PRODUCE
&& t != (int)SkillType.TYPE_LIVE;
}
}
}
@@ -5,6 +5,7 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using static Unity.Burst.Intrinsics.X86.Avx;
namespace CSNetwork.C2SCommand
{
@@ -239,7 +240,6 @@ namespace CSNetwork.C2SCommand
byte byDir, ushort wStamp, ushort iTime
)
{
_logger.Log(LogType.Warning, $"HoangDev : vDest : {vDest}\n speed {FloatToFix8(FloatToFix8(fSpeed))} \n useTime : {iTime}\n moveMode: {iMoveMode} \n stamp: {wStamp}");
var cmd = new CMD_StopMove
{
vCurPos = vDest,
@@ -251,6 +251,25 @@ namespace CSNetwork.C2SCommand
};
return SerializeCommand(CommandID.STOP_MOVE, cmd);
}
public static Octets CreatePlayerCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int aTargets)
{
var cmd = new CMD_CastSkill
{
skillId = idSkill,
pvpMask = byPVPMask,
targetCount = (byte)iNumTarget,
};
if(iNumTarget > 0)
{
if (iNumTarget > 0)
{
cmd.targets = new int[iNumTarget];
cmd.targets[0] = aTargets;
}
}
return SerializeCommand(CommandID.CAST_SKILL, cmd);
}
public static short FloatToFix8(float x)
{
return (short)(x * 256.0f + 0.5f);
@@ -609,7 +609,7 @@ namespace CSNetwork.GPDataType
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_host_attacked
public struct cmd_host_attacked
{
public int idAttacker;
public int iDamage;
@@ -754,7 +754,22 @@ namespace CSNetwork.GPDataType
public override string ToString() => $"({x}, {y}, {z})";
}
// PVP mask
[Flags]
public enum PVPMask
{
GP_PVPMASK_FORCE = 0x0001, // Ç¿Á¦¹¥»÷
GP_PVPMASK_NOMAFIA = 0x0002,
GP_PVPMASK_NOWHITE = 0x0004,
GP_PVPMASK_NOALLIANCE = 0x0008,
GP_PVPMASK_NOFORCE = 0x0010,//²»¹¥»÷Í¬ÊÆÁ¦µÄ
GP_BLSMASK_NORED = 0x0008,
GP_BLSMASK_NOMAFIA = 0x0010,
GP_BLSMASK_SELF = 0x0020,
GP_BLSMASK_NOALLIANCE = 0x0040,
GP_BLSMASK_NOFORCE = 0x0080, // ÊÆÁ¦ÆÁ±Î
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_self_info_1
{
@@ -772,19 +787,19 @@ namespace CSNetwork.GPDataType
//TO DO: Check Valid
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_self_info_00
public struct cmd_self_info_00
{
public short sLevel;
public byte State;
public byte Level2;
public int iHP;
public int iMaxHP;
public int iMP;
public int iMaxMP;
public int iExp;
public int iSP;
public int iAP;
public int iMaxAP;
public short sLevel;
public byte State;
public byte Level2;
public int iHP;
public int iMaxHP;
public int iMP;
public int iMaxMP;
public int iExp;
public int iSP;
public int iAP;
public int iMaxAP;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
@@ -940,7 +955,7 @@ namespace CSNetwork.GPDataType
{
public int cash_amount;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct cmd_unfreeze_ivtr_slot
{
@@ -993,8 +1008,8 @@ namespace CSNetwork.GPDataType
public int expire_date;
public uint amount;
public uint slot_amount;
public byte where;
public byte index;
public byte where;
public byte index;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct cmd_pickup_item
@@ -883,6 +883,15 @@ namespace CSNetwork
C2SCommandFactory.CreatePlayerMove(vCurPos, vDest, (ushort)iTime, fSpeed, (byte)iMoveMode, wStamp);
SendProtocol(gamedatasend);
}
public void c2s_CmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int aTargets)
{
gamedatasend gamedatasend = new gamedatasend();
gamedatasend.Data =
C2SCommandFactory.CreatePlayerCastSkill(idSkill, byPVPMask, iNumTarget, aTargets);
SendProtocol(gamedatasend);
}
public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode,
byte byDir, ushort wStamp, int iTime)
@@ -894,6 +903,7 @@ namespace CSNetwork
SendProtocol(gamedatasend);
}
public void SendChatData(byte cChannel, in string szMsg, int iPack, int iSlot)
{
publicchat publicChat = new publicchat();
@@ -72,7 +72,10 @@ namespace BrewMonster.Network
Instance._ip = ip;
Instance._port = port;
}
public static void c2s_CmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int aTargets)
{
Instance._gameSession.c2s_CmdCastSkill(idSkill, byPVPMask, iNumTarget, aTargets);
}
public static async Task Login(string username, string password, Action<bool> onLoginComplete = null)
{
Instance._username = username;
@@ -0,0 +1,23 @@
using UnityEngine;
namespace BrewMonster
{
public enum ActionContextType
{
AC_NONE = 0,
AC_RIDETOFLY,
AC_RIDETOSKILL,
AC_FLYTORIDE,
AC_RIDETOUSETARGETITEM,
}
public class CECActionContext
{
public ActionContextType ContextType { get; set; }
public bool IsContext(ActionContextType contextType)
{
return ContextType == contextType;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 540c5cee0c186fc468d10477343db08f
@@ -0,0 +1,99 @@
using BrewMonster.Assets.PerfectWorld.Scripts.Players;
using UnityEngine;
namespace BrewMonster
{
public class CECActionSwitcher : CECActionSwitcherBase
{
private System.Collections.Generic.List<CECActionContext> m_actionContexts =
new System.Collections.Generic.List<CECActionContext>();
public CECActionSwitcher(CECHostPlayer pHost) : base(pHost)
{
}
public void Tick(float deltaTime)
{
ProcessMessage();
// Convert DWORD to uint for milliseconds (Unity deltaTime is in seconds)
uint dt = (uint)(deltaTime * 1000f);
// Iterate backwards to safely remove items during iteration
for (int i = m_actionContexts.Count - 1; i >= 0; i--)
{
CECActionContext pContext = m_actionContexts[i];
if (pContext != null)
{
/*pContext.Update(dt);
if (pContext.NeedBeRemoved())
{
// In C#, we just remove from list - GC handles cleanup
m_actionContexts.RemoveAt(i);
}*/
}
else
{
m_actionContexts.RemoveAt(i);
}
}
}
public void ProcessMessage()
{
for (int i = 0; i < m_msgs.Count; i++)
{
EMsgActionSwitcher eMsg = (EMsgActionSwitcher)m_msgs[i];
switch (eMsg)
{
/* case EMsgActionSwitcher.MSG_FLY:
{
OnFly();
}
break;
case EMsgActionSwitcher.MSG_MOUNTPET:
{
OnRide();
}
break;*/
case EMsgActionSwitcher.MSG_CASTSKILL:
{
OnCastSkill();
}
break;
}
}
m_msgs.Clear();
}
public void OnCastSkill()
{
RemoveRideFlyRelatedContext();
}
public void RemoveRideFlyRelatedContext()
{
// Iterate backwards to safely remove items while iterating
for (int i = m_actionContexts.Count - 1; i >= 0; i--)
{
CECActionContext context = m_actionContexts[i];
if (context != null)
{
bool isRelated = context.IsContext(ActionContextType.AC_FLYTORIDE)
|| context.IsContext(ActionContextType.AC_RIDETOSKILL)
|| context.IsContext(ActionContextType.AC_RIDETOFLY)
|| context.IsContext(ActionContextType.AC_RIDETOUSETARGETITEM);
if (isRelated)
{
// In C#, we don't need explicit delete - GC will handle it
// But if context implements IDisposable, call Dispose() here
if (context is System.IDisposable disposable)
{
disposable.Dispose();
}
m_actionContexts.RemoveAt(i);
}
}
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 04f7866e9c0cb8f49828b88ed3f8caeb
@@ -10,24 +10,25 @@ namespace BrewMonster.Assets.PerfectWorld.Scripts.Players
{
CECHostPlayer m_pHostPlayer;
bool m_bCanAddMsg;
List<(int,int)> m_msgs = new List<(int,int)>();
protected List<int> m_msgs = new List<int>();
List<CECActionContext> m_actionContexts;
public CECActionSwitcherBase(CECHostPlayer pHost)
{
m_pHostPlayer = pHost;
}
public virtual bool OnRideToSkillAction(int skill, bool bCom, int iSel, int iForceAtk) { return false; }
public bool CanAddMessage() {return m_bCanAddMsg;}
public bool CanAddMessage() { return m_bCanAddMsg; }
public void PostMessge(int msg)
{
/* if (CanAddMessage())
m_msgs.UniquelyAdd(msg);*/
if (CanAddMessage())
m_msgs.UniquelyAdd(msg);
}
}
public enum EMsgActionSwitcher
{
MSG_FLY = 0,
MSG_MOUNTPET,
MSG_CASTSKILL,
};
{
MSG_FLY = 0,
MSG_MOUNTPET,
MSG_CASTSKILL,
};
}
@@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.VisualScripting;
using static BrewMonster.SkillArrayWrapper;
namespace BrewMonster.Assets.PerfectWorld.Scripts.Skills
{
@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BrewMonster.Assets.PerfectWorld.Scripts.Skills
{
public class CECSkill
{
public int m_idSkill;
public int m_iLevel; // Skill level
public int m_iCoolCnt; // Cooling time counter
public int m_iCoolTime; // Total cooling time
public bool m_bCooling; // In cooling state
public int m_iChargeCnt; // Charging time counter
public int m_iChargeMax;
public bool m_bCharging;
public CECSkill(int id, int iLevel)
{
m_idSkill = id;
}
public int GetSkillID() { return m_idSkill; }
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: a8ba4073670fa0344912b466d9ad7cba
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Unity.VisualScripting;
namespace BrewMonster.Scripts.Skills
@@ -99,7 +100,7 @@ namespace BrewMonster.Scripts.Skills
public class SkillStr
{
public virtual ushort[] Find(int id) { return new ushort[0]; }
public virtual string Find(int id) { return ""; }
}
public enum SKILL_STATE
@@ -147,14 +148,14 @@ namespace BrewMonster.Scripts.Skills
{
return new Dictionary<uint, int>();
}
public virtual ushort[] GetName() { return null; }
public virtual string GetName() { return null; }
public virtual byte[] GetNativeName() { return null; }
// ?,skill_type
public virtual byte GetType() { return 1; }
// ͼ
public virtual byte[] GetIcon() { return null; }
public virtual string GetIcon() { return null; }
// ˵
public virtual ushort[] GetIntroduction(string buf, int len, SkillStr table) { return new ushort[0]; }
public virtual string GetIntroduction(StringBuilder buf, int len, SkillStr table) { return ""; }
// ְҵ
public virtual int GetCls() { return -1; }
// ȴʱλ
+8 -3
View File
@@ -2,7 +2,6 @@ using System.Collections.Generic;
namespace BrewMonster.Scripts.Skills
{
public class Range
{
/// <summary>0=point 1=line 2=self sphere 3=target sphere 4=cone 5=self</summary>
@@ -54,12 +53,18 @@ namespace BrewMonster.Scripts.Skills
{
return stub.GetCls();
}
public override Dictionary<uint , int > GetJunior()
public override Dictionary<uint, int> GetJunior()
{
return stub.is_senior != 0 ? stub.pre_skills : new Dictionary<uint, int>();
return stub.is_senior != 0 ? stub.pre_skills : new Dictionary<uint, int>();
}
public override string GetIcon()
{
return stub.GetIcon();
}
public override string GetName() { return stub.GetName(); }
}
public abstract class SkillStub
{
public const int MIN_LEVEL = 1;
@@ -0,0 +1,254 @@
using BrewMonster.Assets.PerfectWorld.Scripts.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BrewMonster.Assets.PerfectWorld.Scripts.UI
{
public class CECUIConfig : Singleton<CECUIConfig>
{
GameUI m_gameUI;
public GameUI GetGameUI()
{
return m_gameUI;
}
}
public struct RandomMapItem
{
public int itemID;
public int count;
public RandomMapItem(int itemId, int itemCount)
{
itemID = itemId;
count = itemCount;
}
}
// Wallow hint info type
public enum WallowHintType
{
WHT_DEFAULT = 0, // Default mode
WHT_KOREA = 1, // Korean mode
}
// Recommend shop item type
public enum RecommendShopItemType
{
RECOMMEND_REFINE_1, // Equipment refinement level 1
RECOMMEND_REFINE_2, // Equipment refinement level 2
RECOMMEND_REFINE_3, // Equipment refinement level 3
RECOMMEND_REFINE_4, // Equipment refinement level 4
RECOMMEND_REFINE_5, // Equipment refinement level 5
RECOMMEND_SAVE_LIFE, // Save life
RECOMMEND_ACTIVITY, // Activity reminder
}
// Game logic and UI related configuration
public struct GameUI
{
public bool bMailToFriendsSwitch; // Mail to friends
public int nMailToFriendsDaysNoLogin; // Days not logged in to trigger mail to friends
public int nMailToFriendsLevel; // Level required to use mail to friends
public int nMailToFriendsDaysSendMail; // Days interval to send mail again
public bool bActivityReminder; // Activity reminder
public int nActivityReminderLevel; // Level requirement
public int nActivityReminderMaxLevelSoFar; // Historical maximum level requirement
public int nActivityReminderLevel2; // Second level requirement
public int nActivityReminderReincarnationTimes;// Reincarnation times requirement
public int nActivityReminderRealmLevel; // Realm level requirement
public int nActivityReminderReputation; // Reputation requirement
public DateTime tActivityReminderStartTime; // Start time (local time)
public DateTime tActivityReminderEndTime; // End time (local time)
public bool bEnableTalkToGM; // Enable sending non-standard messages to GM
public bool bEnableTrashPwdRemind; // Enable trash password reminder for users
public WallowHintType nWallowHintType; // Wallow hint display type
public bool bEnableIE; // Enable IE to display web pages
public bool bEnableShowIP; // Whether to show last login information
public bool bEnableCompleteAccount; // Whether to show account completion information
public bool bEnableFortressBuildDestroy; // Whether to enable fortress build/destroy facilities
public List<int> nCountryWarBonus; // Country war bonus levels
public bool bShowNameInCountryWar; // Whether to show player names in country war
public int nCountryWarEnterLevel; // Country war entry level requirement
public int nCountryWarEnterItem; // Country war entry item id
public int nCountryWarEnterItemCount; // Country war entry item count
public bool bEnableQuickPay; // Enable quick payment
public int nEquipMarkMinInkNum; // Minimum ink number required for equipment mark modification
public bool bEnableReportPlayerSpeakToGM; // Enable reporting player speech to GM
public bool bEnableReportPluginWithFeedback; // Enable plugin reporting with feedback
public uint nGTLoginCoolDown; // Game GT account login cooldown time (in seconds)
public bool bEnableGTOnSpecialServer; // Whether to enable GT on special servers
public int nCrossServerEnterLevel; // Cross server entry level requirement
public int nCrossServerEnterLevel2; // Cross server entry level requirement 2
public bool bEnableWebTradeSort; // Enable sorting when searching for items in web trade
public bool bEnablePlayerRename; // Enable player rename
public bool bEnableCheckNewbieGift; // Enable newbie gift display
public bool bEnableQShopFilter; // Enable QShop filter display function (filtered by player level)
public bool bEnableGivingFor; // Enable QShop gift and receive function
public bool bEnableGivingForTaskLimitedItem; // Enable QShop certain task-limited items gift and receive function
public List<int> nCountryWarPlayerLimit; // Country war battle player count limit
public int nCountryWarKingMaxDomainLimit; // Country war king maximum domain limit
public int nFashionSplitCost; // Fashion split cost
public bool bEnableTouch; // Touch shop enable
public bool bEnableOptimize; // Whether to enable client optimization
public int nMemoryUsageLow; // Low memory value, system will automatically restore normal model
public int nMemoryUsageHigh; // High memory value, system will automatically reduce level model
public int nAutoSimplifySpeed; // Auto simplify speed after long time
public List<int> nTouchEnabledMap; // TOUCH shop enabled maps
public bool bEnableTWRecharge; // Shop recharge button (for Taiwan region recharge platform)
public string strTWRechargeAppID; // Taiwan recharge platform parameter AppID (developer code)
public string strTWRechargeGame; // Taiwan recharge platform parameter Game (game code)
public string strTWRechargeKey; // Taiwan recharge platform parameter Key (additional verification key)
public bool bEnableTitle; // Enable Title UI
public List<int> nAutoTeamTransmitEnabledMap; // Auto team transmit enabled map IDs
public int nChariotApplyLevel; // Chariot application level requirement
public int nChariotApplyLevel2; // Chariot application level requirement 2
public int nChariotApplyReincarnation; // Chariot application reincarnation requirement
public int nChariotReviveTimeout; // Chariot revive timeout
public int nChariotAmount; // Chariot battle chariot count
public int nHistoryQueryTimeInterval; // History query time interval
public bool bEnableAutoWiki; // Auto wiki enable
public int nExitAutoExtractWikiStateTime; // Auto wiki: exit auto extract time
public int nCloseWikiPopDlgTime; // Auto wiki: close time
public int nCloseWikiMsgInfoTime; // Auto wiki: message disappear time
public int nOpenWikiPopDlgTime; // Auto wiki: open time
public List<int> nTaskDisabledInMiniClient; // Tasks disabled in mini client
public List<int> nItemDisabledInMiniClient; // Items disabled in mini client
public List<int> nMeridianFreeItem; // Meridian free item ID list
public List<int> nMeridianNotFreeItem; // Meridian not free item ID list
public int nMonsterSpiritGatherTimesPerWeekMax;// Maximum monster spirit gather times per week
public bool bEnableAutoPolicy; // Auto policy system enable
public bool bEnablePWService; // Enable PW service page
public bool bEnableActionSwitch; // Action switch enable (attack->defense, defense->attack, auto return before using skill)
public int nCountryWarLiveShowUpdateInterval; // Country war live show data update interval
public List<int> nDefaultSystemModuleIndex; // System module default function index
public bool bEnableRecommendQShopItem; // Recommend QShop items when appropriate
public List<int> nRecommendShopItems; // Recommended shop items list
public bool bEnableRandShop; // Whether to enable random shop (in new version, old version follows original agreement, this function is disabled)
public int nPokerShopConfig; // Poker shop config table ID
public int nPokerShopLevelLimit; // Poker shop usage level requirement
public int nContributionTaskLevelLimit; // Contribution task system usage level requirement
public List<string> strFashionShopAdImage; // Fashion shop advertisement images
public bool bEnableQShopFashionShop; // QShop fashion shop enable
public bool bEnableBackShopFashionShop; // Back shop fashion shop enable
public bool bEnableCeilPriceBeforeDiscountToGold; // Shop discount item original price only shows gold, if original price can be converted to gold, show original price (except market)
public List<string> strFullScreenGfxForeground; // Full screen effect foreground effect
public List<string> strFullScreenGfxBackground; // Full screen effect background effect
public List<int> nRandomMaps; // Random maps
public List<int> nTaskIDForDisableWayPointUITips; // Task IDs that need to disable waypoint tip display when doing these tasks
public RandomMapItem DefaultRandomMapItem; // Default random map item distribution item
public Dictionary<int, RandomMapItem> SpecialRandomMapItems; // Specific random map special random map item distribution item
public int nMaxFriendRemarksNameLength; // Friend remarks name maximum length
public bool bEnableQShopFashionShopFlashSale; // QShop fashion shop flash sale enable
public bool bEnableBackShopFashionShopFlashSale; // Back shop fashion shop flash sale enable
public string strQShopFashionShopFlashSaleTitle; // QShop fashion shop flash sale button title
public string strBackShopFashionShopFlashSaleTitle; // Back shop fashion shop flash sale button title
public bool bEnablePlayerChangeGender;
public int GetCountryWarBonusLevel(int currentBonus)
{
// Get country war current bonus level, starting from level 0, return -1 if invalid
int level = -1;
while (level + 1 < nCountryWarBonus.Count && currentBonus > nCountryWarBonus[level + 1])
{
level++;
}
return level;
}
public int GetCountryWarPlayerLimit(int warType)
{
if (warType >= 0 && warType < nCountryWarPlayerLimit.Count)
{
return nCountryWarPlayerLimit[warType];
}
return 0;
}
public bool GetCanShowTouchShop(int idInst)
{
return nTouchEnabledMap.Contains(idInst);
}
public bool IsTaskDisabledInMiniClient(int task_id)
{
return nTaskDisabledInMiniClient.Contains(task_id);
}
public bool IsItemDisabledInMiniClient(int item_id)
{
return nItemDisabledInMiniClient.Contains(item_id);
}
public bool IsMeridianFreeItem(int item_id)
{
return nMeridianFreeItem.Contains(item_id);
}
public bool IsMeridianNotFreeItem(int item_id)
{
return nMeridianNotFreeItem.Contains(item_id);
}
public bool IsRandomMap(int mapid)
{
return nRandomMaps.Contains(mapid);
}
public int GetRandomMapCount()
{
return nRandomMaps.Count;
}
public int GetRandomMapID(int idx)
{
if (idx >= 0 && idx < nRandomMaps.Count)
{
return nRandomMaps[idx];
}
return 0;
}
public bool GetRandomMapItemInfo(int mapID, out RandomMapItem info)
{
if (SpecialRandomMapItems.TryGetValue(mapID, out info))
{
return true;
}
// If not found in special items, return false (not using default)
info = default(RandomMapItem);
return false;
}
public int GetTaskIDDisableWayPointsUITipsCount()
{
return nTaskIDForDisableWayPointUITips.Count;
}
public int GetTaskIDDisableWayPointsUITips(int idx)
{
if (idx >= 0 && idx < nTaskIDForDisableWayPointUITips.Count)
{
return nTaskIDForDisableWayPointUITips[idx];
}
return 0;
}
public int GetRecommendShopItem(RecommendShopItemType type)
{
int index = (int)type;
if (index >= 0 && index < nRecommendShopItems.Count)
{
return nRecommendShopItems[index];
}
return 0;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2db7d5cd42386b448927779069efcf1c
+321 -231
View File
@@ -1,5 +1,6 @@
using BrewMonster;
using BrewMonster.Assets.PerfectWorld.Scripts.Players;
using BrewMonster.Assets.PerfectWorld.Scripts.Skills;
using BrewMonster.Managers;
using BrewMonster.Network;
using BrewMonster.Scripts;
@@ -56,9 +57,13 @@ public class CECHostPlayer : CECPlayer
private int m_iRoleCreateTime;
private int m_iRoleLastLoginTime; // Role last login time
private int m_iAccountTotalCash;
private List<CECObject> m_aTabSels = new List<CECObject>();
private List<CECSkill> m_aPtSkills = new List<CECSkill>();
private List<CECSkill> m_aEquipSkills = new List<CECSkill>();
private List<CECSkill> m_aGoblinSkills = new List<CECSkill>();
private CECSkill m_pPrepSkill;
private float playerSpeed = 5.0f;
private float jumpHeight = 1.5f;
private float gravityValue = -9.81f;
@@ -965,9 +970,9 @@ public class CECHostPlayer : CECPlayer
return;
}
if (false /*CECUIConfig::Instance().GetGameUI().bEnableActionSwitch*/)
{ //m_pActionSwitcher = new CECActionSwitcher(this);
if (true /*CECUIConfig::Instance().GetGameUI().bEnableActionSwitch*/)
{
m_pActionSwitcher = new CECActionSwitcher(this);
}
else
m_pActionSwitcher = new CECActionSwitcherBase(this);
@@ -1455,274 +1460,308 @@ public class CECHostPlayer : CECPlayer
{
return m_ExtProps.mv.swim_speed;
}
public bool ApplySkillShortcut(int idSkill, bool bCombo = false /* false */,
int idSelTarget = 0/* 0 */, int iForceAtk = -1/* -1 */)
public bool ApplySkillShortcut(int idSkill, bool bCombo = false /* false */,
int idSelTarget = 0/* 0 */, int iForceAtk = -1/* -1 */)
{
//StackChecker::ACTrace(4);
/* if (m_pActionSwitcher != null)
m_pActionSwitcher.PostMessge(EMsgActionSwitcher.MSG_CASTSKILL);
if (m_pActionSwitcher != null)
m_pActionSwitcher.PostMessge((int)EMsgActionSwitcher.MSG_CASTSKILL);
// Return-town skill is very special, handle it separately
if (idSkill == ID_RETURNTOWN_SKILL)
return ReturnToTargetTown(0, bCombo);
//if (idSkill == ID_RETURNTOWN_SKILL)
// return ReturnToTargetTown(0, bCombo);
if (idSkill == ID_SUMMONPLAYER_SKILL)
return SummonPlayer(idSelTarget, bCombo);
//if (idSkill == ID_SUMMONPLAYER_SKILL)
// return SummonPlayer(idSelTarget, bCombo);
if (!CanDo(CANDO_SPELLMAGIC))
return false;
//if (!CanDo(CANDO_SPELLMAGIC))
// return false;
if (InSlidingState())
return false;
//if (InSlidingState())
// return false;
if (!bCombo)
ClearComboSkill();
//ClearComboSkill();
if (!idSelTarget)
idSelTarget = m_idSelTarget;
if (idSelTarget == 0)
idSelTarget = m_idSelTarget;
CECSkill* pSkill = GetPositiveSkillByID(idSkill);
if (!pSkill) pSkill = GetEquipSkillByID(idSkill);
if (!pSkill) pSkill = CECComboSkillState::Instance().GetInherentSkillByID(idSkill);
if (!pSkill)
CECSkill pSkill = GetPositiveSkillByID(idSkill);
if (pSkill == null) pSkill = GetEquipSkillByID(idSkill);
if (pSkill == null) pSkill = CECComboSkillState.Instance.GetInherentSkillByID((uint)idSkill);
if (pSkill == null)
{
ASSERT(0);
return false;
}
// If we press a chargeable skill again when it's being charged,
// we cast it out at once
if (IsSpellingMagic() && m_pCurSkill && m_pCurSkill->IsCharging() &&
m_pCurSkill->GetSkillID() == pSkill->GetSkillID())
{
m_pCurSkill->EndCharging();
g_pGame->GetGameSession()->c2s_CmdContinueAction();
return true;
}
//// If we press a chargeable skill again when it's being charged,
//// we cast it out at once
//if (IsSpellingMagic() && m_pCurSkill && m_pCurSkill->IsCharging() &&
// m_pCurSkill->GetSkillID() == pSkill->GetSkillID())
//{
// m_pCurSkill->EndCharging();
// g_pGame->GetGameSession()->c2s_CmdContinueAction();
// return true;
//}
int iCon = CheckSkillCastCondition(pSkill);
if (iCon)
{
ProcessSkillCondition(iCon);
return false;
}
//int iCon = CheckSkillCastCondition(pSkill);
//if (iCon)
//{
// ProcessSkillCondition(iCon);
// return false;
//}
// Get force attack flag
bool bForceAttack;
if (iForceAtk < 0)
bForceAttack = glb_GetForceAttackFlag(NULL);
else
bForceAttack = iForceAtk > 0 ? true : false;
//// Get force attack flag
bool bForceAttack = true;
//if (iForceAtk < 0)
// bForceAttack = glb_GetForceAttackFlag(0);
//else
// bForceAttack = iForceAtk > 0 ? true : false;
// Check negative effect skill
if (pSkill->GetType() == CECSkill::TYPE_ATTACK || pSkill->GetType() == CECSkill::TYPE_CURSE)
{
if (idSelTarget == m_PlayerInfo.cid)
{
// Host cannot spell negative effect magic to himself.
g_pGame->GetGameRun()->AddFixedChannelMsg(FIXMSG_TARGETWRONG, GP_CHAT_FIGHT);
return false;
}
else if (idSelTarget)
{
if (AttackableJudge(idSelTarget, bForceAttack) != 1)
return false;
}
}
//// Check negative effect skill
//if (pSkill->GetType() == CECSkill::TYPE_ATTACK || pSkill->GetType() == CECSkill::TYPE_CURSE)
//{
// if (idSelTarget == m_PlayerInfo.cid)
// {
// // Host cannot spell negative effect magic to himself.
// g_pGame->GetGameRun()->AddFixedChannelMsg(FIXMSG_TARGETWRONG, GP_CHAT_FIGHT);
// return false;
// }
// else if (idSelTarget)
// {
// if (AttackableJudge(idSelTarget, bForceAttack) != 1)
// return false;
// }
//}
// Check whether target type match
int idCastTarget = idSelTarget;
int iTargetType = pSkill->GetTargetType();
//// Check whether target type match
//int idCastTarget = idSelTarget;
//int iTargetType = pSkill->GetTargetType();
if (pSkill->GetType() == CECSkill::TYPE_BLESS ||
pSkill->GetType() == CECSkill::TYPE_NEUTRALBLESS)
{
if (!iTargetType || !ISPLAYERID(idSelTarget))
idCastTarget = m_PlayerInfo.cid;
//if (pSkill->GetType() == CECSkill::TYPE_BLESS ||
// pSkill->GetType() == CECSkill::TYPE_NEUTRALBLESS)
//{
// if (!iTargetType || !ISPLAYERID(idSelTarget))
// idCastTarget = m_PlayerInfo.cid;
// In some case, we shouldn't add bless effect to other players
if (ISPLAYERID(idCastTarget) && idCastTarget != m_PlayerInfo.cid)
{
// If host has set bless skill filter only to himself, bless skill couldn't add to other players
BYTE byBLSMask = glb_BuildBLSMask();
// // In some case, we shouldn't add bless effect to other players
// if (ISPLAYERID(idCastTarget) && idCastTarget != m_PlayerInfo.cid)
// {
// // If host has set bless skill filter only to himself, bless skill couldn't add to other players
// BYTE byBLSMask = glb_BuildBLSMask();
if (pSkill->GetRangeType() == CECSkill::RANGE_POINT)
{
if (!IsTeamMember(idCastTarget))
{
if (byBLSMask & GP_BLSMASK_SELF)
idCastTarget = m_PlayerInfo.cid;
else
{
CECElsePlayer* pPlayer = (CECElsePlayer*)g_pGame->GetGameRun()->GetWorld()->GetPlayerMan()->GetPlayer(idCastTarget);
if (!pPlayer)
{
// Ä¿±êÏûʧ
return false;
}
// if (pSkill->GetRangeType() == CECSkill::RANGE_POINT)
// {
// if (!IsTeamMember(idCastTarget))
// {
// if (byBLSMask & GP_BLSMASK_SELF)
// idCastTarget = m_PlayerInfo.cid;
// else
// {
// CECElsePlayer* pPlayer = (CECElsePlayer*)g_pGame->GetGameRun()->GetWorld()->GetPlayerMan()->GetPlayer(idCastTarget);
// if (!pPlayer)
// {
// // Ä¿±êÏûʧ
// return false;
// }
if (pPlayer->IsInvader() || pPlayer->IsPariah())
{
if (byBLSMask & GP_BLSMASK_NORED)
idCastTarget = m_PlayerInfo.cid;
}
// if (pPlayer->IsInvader() || pPlayer->IsPariah())
// {
// if (byBLSMask & GP_BLSMASK_NORED)
// idCastTarget = m_PlayerInfo.cid;
// }
if (!IsFactionMember(pPlayer->GetFactionID()))
{
if (byBLSMask & GP_BLSMASK_NOMAFIA)
idCastTarget = m_PlayerInfo.cid;
}
// if (!IsFactionMember(pPlayer->GetFactionID()))
// {
// if (byBLSMask & GP_BLSMASK_NOMAFIA)
// idCastTarget = m_PlayerInfo.cid;
// }
if (!IsFactionAllianceMember(pPlayer->GetFactionID()))
{
if (byBLSMask & GP_BLSMASK_NOALLIANCE)
idCastTarget = m_PlayerInfo.cid;
}
if (GetForce() != pPlayer->GetForce())
{
if (byBLSMask & GP_BLSMASK_NOFORCE)
idCastTarget = m_PlayerInfo.cid;
}
}
}
}
// if (!IsFactionAllianceMember(pPlayer->GetFactionID()))
// {
// if (byBLSMask & GP_BLSMASK_NOALLIANCE)
// idCastTarget = m_PlayerInfo.cid;
// }
// if (GetForce() != pPlayer->GetForce())
// {
// if (byBLSMask & GP_BLSMASK_NOFORCE)
// idCastTarget = m_PlayerInfo.cid;
// }
// }
// }
// }
// If host is in duel, bless skill couldn't add to opponent
if (IsInDuel() && idSelTarget == m_pvp.idDuelOpp)
idCastTarget = m_PlayerInfo.cid;
// // If host is in duel, bless skill couldn't add to opponent
// if (IsInDuel() && idSelTarget == m_pvp.idDuelOpp)
// idCastTarget = m_PlayerInfo.cid;
// If host is in battle, bless skill couldn't add to enemies
if (IsInBattle())
{
CECElsePlayer* pPlayer = m_pPlayerMan->GetElsePlayer(idCastTarget);
if (!InSameBattleCamp(pPlayer))
idCastTarget = m_PlayerInfo.cid;
}
}
}
else if (pSkill->GetType() == CECSkill::TYPE_BLESSPET)
{
CECPet* pPet = g_pGame->GetGameRun()->GetWorld()->GetNPCMan()->GetPetByID(idSelTarget);
if (!pPet || pPet->GetMasterID() == GetCharacterID())
{
// Spell skill on host's pet
CECPetData* pPetData = m_pPetCorral->GetActivePet();
if (!pPetData ||
pPetData->GetClass() != GP_PET_CLASS_COMBAT &&
pPetData->GetClass() != GP_PET_CLASS_SUMMON &&
pPetData->GetClass() != GP_PET_CLASS_EVOLUTION)
return false;
// // If host is in battle, bless skill couldn't add to enemies
// if (IsInBattle())
// {
// CECElsePlayer* pPlayer = m_pPlayerMan->GetElsePlayer(idCastTarget);
// if (!InSameBattleCamp(pPlayer))
// idCastTarget = m_PlayerInfo.cid;
// }
// }
//}
//else if (pSkill->GetType() == CECSkill::TYPE_BLESSPET)
//{
// CECPet* pPet = g_pGame->GetGameRun()->GetWorld()->GetNPCMan()->GetPetByID(idSelTarget);
// if (!pPet || pPet->GetMasterID() == GetCharacterID())
// {
// // Spell skill on host's pet
// CECPetData* pPetData = m_pPetCorral->GetActivePet();
// if (!pPetData ||
// pPetData->GetClass() != GP_PET_CLASS_COMBAT &&
// pPetData->GetClass() != GP_PET_CLASS_SUMMON &&
// pPetData->GetClass() != GP_PET_CLASS_EVOLUTION)
// return false;
idCastTarget = m_pPetCorral->GetActivePetNPCID();
}
// Only fighting pet can be blessed.
if (pPet && !pPet->CanBeAttacked())
return false;
}
else
{
if (iTargetType != 0 && !idCastTarget)
return false;
}
// idCastTarget = m_pPetCorral->GetActivePetNPCID();
// }
// // Only fighting pet can be blessed.
// if (pPet && !pPet->CanBeAttacked())
// return false;
//}
//else
//{
// if (iTargetType != 0 && !idCastTarget)
// return false;
//}
// iTargetType == 4 means target must be pet. The problem is that pet will
// disappear from world after it died, so GetWorld()->GetObject() will return
// NULL when host spells revive-pet skill on his dead pet. So, the target
// type of revive-pet skill should be 0
if (iTargetType)
{
// Target shoundn't be a corpse ?
int iAliveFlag = 0;
if (iTargetType == 1)
iAliveFlag = 1;
else if (iTargetType == 2)
iAliveFlag = 2;
//// iTargetType == 4 means target must be pet. The problem is that pet will
//// disappear from world after it died, so GetWorld()->GetObject() will return
//// NULL when host spells revive-pet skill on his dead pet. So, the target
//// type of revive-pet skill should be 0
//if (iTargetType)
//{
// // Target shoundn't be a corpse ?
// int iAliveFlag = 0;
// if (iTargetType == 1)
// iAliveFlag = 1;
// else if (iTargetType == 2)
// iAliveFlag = 2;
CECObject* pObject = g_pGame->GetGameRun()->GetWorld()->GetObject(idCastTarget, iAliveFlag);
if (!pObject)
return false;
}
// CECObject* pObject = g_pGame->GetGameRun()->GetWorld()->GetObject(idCastTarget, iAliveFlag);
// if (!pObject)
// return false;
//}
if (!IsMeleeing() && !IsSpellingMagic() &&
(!iTargetType || idCastTarget == m_PlayerInfo.cid))
{
// Cast this skill need't checking cast distance
if (!pSkill->ReadyToCast())
return false;
//if (!IsMeleeing() && !IsSpellingMagic() &&
// (!iTargetType || idCastTarget == m_PlayerInfo.cid))
//{
// // Cast this skill need't checking cast distance
// if (!pSkill->ReadyToCast())
// return false;
// Prepare to cast skill, if skill isn't INSTANT and FLASHMOVE,
// we must stop moving and stand
if (!pSkill->IsInstant() && pSkill->GetType() != CECSkill::TYPE_FLASHMOVE)
{
if (!NaturallyStopMoving())
return false; // Couldn't stop naturally, so cancel casting skill
}
else if (pSkill->GetType() == CECSkill::TYPE_FLASHMOVE)
{
if (!CanDo(CANDO_FLASHMOVE))
return false;
}
// // Prepare to cast skill, if skill isn't INSTANT and FLASHMOVE,
// // we must stop moving and stand
// if (!pSkill->IsInstant() && pSkill->GetType() != CECSkill::TYPE_FLASHMOVE)
// {
// if (!NaturallyStopMoving())
// return false; // Couldn't stop naturally, so cancel casting skill
// }
// else if (pSkill->GetType() == CECSkill::TYPE_FLASHMOVE)
// {
// if (!CanDo(CANDO_FLASHMOVE))
// return false;
// }
m_pPrepSkill = pSkill;
CastSkill(m_PlayerInfo.cid, bForceAttack);
}
else if (IsSpellingMagic() && m_pCurSkill == pSkill)
{
// If we are casting the same skill and it's in cooling time
return false;
}
else // Have to trace selected object before cast skill
{
if (!pSkill->ReadyToCast())
return false;
// m_pPrepSkill = pSkill;
CastSkill(m_PlayerInfo.cid, bForceAttack);
//}
//else if (IsSpellingMagic() && m_pCurSkill == pSkill)
//{
// // If we are casting the same skill and it's in cooling time
// return false;
//}
//else // Have to trace selected object before cast skill
//{
// if (!pSkill->ReadyToCast())
// return false;
if (CECCastSkillWhenMove::Instance().IsSkillSupported(pSkill->GetSkillID(), this) &&
m_pWorkMan->IsMovingToPosition() &&
m_pWorkMan->CanCastSkillImmediately(pSkill->GetSkillID()))
{
m_pPrepSkill = pSkill;
return CastSkill(idCastTarget, bForceAttack);
}
else
{
bool bTraceOK = false;
bool bUseAutoPF = false;
CECPlayerWrapper* pWrapper = CECAutoPolicy::GetInstance().GetPlayerWrapper();
if (CECAutoPolicy::GetInstance().IsAutoPolicyEnabled() && pWrapper->GetAttackError() >= 2)
bUseAutoPF = true;
// if (CECCastSkillWhenMove::Instance().IsSkillSupported(pSkill->GetSkillID(), this) &&
// m_pWorkMan->IsMovingToPosition() &&
// m_pWorkMan->CanCastSkillImmediately(pSkill->GetSkillID()))
// {
// m_pPrepSkill = pSkill;
// return CastSkill(idCastTarget, bForceAttack);
// }
// else
// {
// bool bTraceOK = false;
// bool bUseAutoPF = false;
// CECPlayerWrapper* pWrapper = CECAutoPolicy::GetInstance().GetPlayerWrapper();
// if (CECAutoPolicy::GetInstance().IsAutoPolicyEnabled() && pWrapper->GetAttackError() >= 2)
// bUseAutoPF = true;
if (!idCastTarget)
{
idCastTarget = GetCharacterID(); // ±ÜÃâË²ÒÆµÈ¼¼ÄÜʱ idCastTarget Ϊ0µ¼Ö CECWorkTrace::CreateTraceTarget ·µ»Ø¿Õ
}
if (CECHPWork * pWork = m_pWorkMan->GetWork(CECHPWork::WORK_TRACEOBJECT))
{
CECHPWorkTrace* pWorkTrace = dynamic_cast<CECHPWorkTrace*>(pWork);
if (pWorkTrace->GetTraceReason() == CECHPWorkTrace::TRACE_SPELL &&
pWorkTrace->GetTarget() == idCastTarget &&
pWorkTrace->GetPrepSkill() == pSkill)
return false; // We are just doing the same thing
// if (!idCastTarget)
// {
// idCastTarget = GetCharacterID(); // ±ÜÃâË²ÒÆµÈ¼¼ÄÜʱ idCastTarget Ϊ0µ¼Ö CECWorkTrace::CreateTraceTarget ·µ»Ø¿Õ
// }
// if (CECHPWork * pWork = m_pWorkMan->GetWork(CECHPWork::WORK_TRACEOBJECT))
// {
// CECHPWorkTrace* pWorkTrace = dynamic_cast<CECHPWorkTrace*>(pWork);
// if (pWorkTrace->GetTraceReason() == CECHPWorkTrace::TRACE_SPELL &&
// pWorkTrace->GetTarget() == idCastTarget &&
// pWorkTrace->GetPrepSkill() == pSkill)
// return false; // We are just doing the same thing
pWorkTrace->SetTraceTarget(pWorkTrace->CreatTraceTarget(idCastTarget, CECHPWorkTrace::TRACE_SPELL, bForceAttack), bUseAutoPF);
pWorkTrace->SetPrepSkill(pSkill);
bTraceOK = true;
}
else if (m_pWorkMan->CanStartWork(CECHPWork::WORK_TRACEOBJECT))
{
CECHPWorkTrace* pWork = (CECHPWorkTrace*)m_pWorkMan->CreateWork(CECHPWork::WORK_TRACEOBJECT);
pWork->SetTraceTarget(pWork->CreatTraceTarget(idCastTarget, CECHPWorkTrace::TRACE_SPELL, bForceAttack), bUseAutoPF);
pWork->SetPrepSkill(pSkill);
m_pWorkMan->StartWork_p1(pWork);
bTraceOK = true;
}
// pWorkTrace->SetTraceTarget(pWorkTrace->CreatTraceTarget(idCastTarget, CECHPWorkTrace::TRACE_SPELL, bForceAttack), bUseAutoPF);
// pWorkTrace->SetPrepSkill(pSkill);
// bTraceOK = true;
// }
// else if (m_pWorkMan->CanStartWork(CECHPWork::WORK_TRACEOBJECT))
// {
// CECHPWorkTrace* pWork = (CECHPWorkTrace*)m_pWorkMan->CreateWork(CECHPWork::WORK_TRACEOBJECT);
// pWork->SetTraceTarget(pWork->CreatTraceTarget(idCastTarget, CECHPWorkTrace::TRACE_SPELL, bForceAttack), bUseAutoPF);
// pWork->SetPrepSkill(pSkill);
// m_pWorkMan->StartWork_p1(pWork);
// bTraceOK = true;
// }
if (!bTraceOK) return false;
}
}*/
// if (!bTraceOK) return false;
// }
//}
return true;
}
private void CastSkill(int idTarget, bool bForceAttack, CECObject pTarget = null)
{
byte byPVPMask = glb_BuildPVPMask(bForceAttack);
UnityGameSession.c2s_CmdCastSkill(m_pPrepSkill.GetSkillID(), byPVPMask, 1, idTarget);
}
public byte glb_BuildPVPMask(bool bForceAttack)
{
byte byMask = 0;
if (bForceAttack)
byMask |= (byte)PVPMask.GP_PVPMASK_FORCE;
else
{
/* CECConfigs pConfigs = EC_Game.GetConfigs();
if (pConfigs->GetGameSettings().bAtk_Player)
{
byMask |= GP_PVPMASK_FORCE;
if (pConfigs->GetGameSettings().bAtk_NoMafia)
byMask |= GP_PVPMASK_NOMAFIA;
if (pConfigs->GetGameSettings().bAtk_NoWhite)
byMask |= GP_PVPMASK_NOWHITE;
if (pConfigs->GetGameSettings().bAtk_NoAlliance)
byMask |= GP_PVPMASK_NOALLIANCE;
if (pConfigs->GetGameSettings().bAtk_NoForce)
byMask |= GP_PVPMASK_NOFORCE;
}*/
}
return byMask;
}
public bool SelectTarget(int idTarget)
{
BMLogger.LogError("HoangDev: HostPlayer SelectTarget");
@@ -1746,7 +1785,45 @@ public class CECHostPlayer : CECPlayer
return bRet;
}
public CECSkill GetPositiveSkillByID(int id, bool bSenior = false)
{
CECSkill pSenior = null;
for (int i = 0; i < m_aPtSkills.Count; i++)
{
if (m_aPtSkills[i].GetSkillID() == id)
return m_aPtSkills[i];
else if (m_aPtSkills[i].GetJunior().Find((uint)id))
pSenior = m_aPtSkills[i];
}
if (bSenior && pSenior != null)
return pSenior;
return null;
}
// C# conversion of CECHostPlayer::GetEquipSkillByID
// Assumes: GetEquipSkillNum() returns the count of equipment skills
// GetEquipSkillByIndex(int) returns a CECSkill at the given index
public CECSkill GetEquipSkillByID(int id)
{
CECSkill pRet = null;
for (int i = 0; i < GetEquipSkillNum(); i++)
{
CECSkill pSkill = GetEquipSkillByIndex(i);
if (pSkill != null && pSkill.GetSkillID() == id)
{
pRet = pSkill;
break;
}
}
return pRet;
}
public int GetEquipSkillNum() { return m_aEquipSkills.Count; }
public CECSkill GetEquipSkillByIndex(int n) { return m_aEquipSkills[n]; }
bool CanSelectTarget(int idTarget)
{
if (idTarget == 0 || idTarget == this.GetCharacterID())
@@ -2038,7 +2115,20 @@ public class CECHostPlayer : CECPlayer
{
m_idSelTarget = id;
}
public bool glb_GetForceAttackFlag(uint pdwParam)
{
/*bool bForceAttack = false;
CECInputCtrl* pInputCtrl = g_pGame->GetGameRun()->GetInputCtrl();
if (pdwParam)
bForceAttack = pInputCtrl->IsCtrlPressed(*pdwParam);
else
bForceAttack = pInputCtrl->KeyIsBeingPressed(VK_CONTROL);
return bForceAttack;*/
return true;
}
//public float GetSwimSpeedSev()
//{
// float fSpeedSev = GetSwimSpeed();
+158 -155
View File
@@ -5,184 +5,187 @@ using System.IO;
using System.Text;
using UnityEngine; // thêm để dùng Resources & TextAsset
public class CECStringTab
namespace BrewMonster
{
private readonly Dictionary<int, string> m_AStrTab = new Dictionary<int, string>();
private readonly Dictionary<int, string> m_WStrTab = new Dictionary<int, string>();
private bool m_bInit = false;
private bool m_bUnicode = false;
public CECStringTab() { }
~CECStringTab() { Release(); }
public bool Init(string szFile, bool bUnicode)
public class CECStringTab
{
Release();
m_bUnicode = bUnicode;
private readonly Dictionary<int, string> m_AStrTab = new Dictionary<int, string>();
private readonly Dictionary<int, string> m_WStrTab = new Dictionary<int, string>();
try
private bool m_bInit = false;
private bool m_bUnicode = false;
public CECStringTab() { }
~CECStringTab() { Release(); }
public bool Init(string szFile, bool bUnicode)
{
bool ok = bUnicode ? LoadWideStrings(szFile) : LoadANSIStrings(szFile);
m_bInit = ok;
return ok;
}
catch (Exception e)
{
Debug.LogError($"[CECStringTab] Init failed: {e}");
Release();
return false;
}
}
m_bUnicode = bUnicode;
public void Release()
{
m_AStrTab.Clear();
m_WStrTab.Clear();
m_bInit = false;
m_bUnicode = false;
}
public string GetANSIString(int n) => m_AStrTab.TryGetValue(n, out var s) ? s : null;
public string GetWideString(int n) => m_WStrTab.TryGetValue(n, out var s) ? s : null;
public string GetWideStringObject(int n) => GetWideString(n);
public bool IsInitialized() => m_bInit;
// ==== Đọc từ Resources thay vì đường dẫn ====
protected bool LoadANSIStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
// Giải mã bytes -> string (ANSI: dùng Encoding.Default)
string content = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: false);
}
protected bool LoadWideStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
// Unity TextAsset mặc định đã decode text UTF8 -> textAsset.text
// nhưng để chắc chắn BOM/Unicode thì đọc từ bytes
string content;
content = ByteToStringUtils.ByteArrayToUnicodeString(textAsset.bytes);
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: true);
}
private static Encoding DetectEncoding(byte[] bom)
{
if (bom.Length >= 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) return Encoding.UTF8;
if (bom.Length >= 2 && bom[0] == 0xFF && bom[1] == 0xFE) return Encoding.Unicode;
if (bom.Length >= 2 && bom[0] == 0xFE && bom[1] == 0xFF) return Encoding.BigEndianUnicode;
return null;
}
private bool ParseIntoDict(StringReader sr, bool isWide)
{
bool bIndexMode = false;
bool bBegan = false;
int autoIndex = 0;
var allLines = new List<string>();
string line;
while ((line = sr.ReadLine()) != null)
{
allLines.Add(line);
}
for (int i = 0; i < allLines.Count; i++)
{
var ln = allLines[i].Trim();
if (ln.Length == 0) continue;
if (ln.Equals("#_index", StringComparison.OrdinalIgnoreCase))
try
{
bIndexMode = true;
bool ok = bUnicode ? LoadWideStrings(szFile) : LoadANSIStrings(szFile);
m_bInit = ok;
return ok;
}
else if (ln.Equals("#_begin", StringComparison.OrdinalIgnoreCase))
catch (Exception e)
{
bBegan = true;
Debug.LogError($"[CECStringTab] Init failed: {e}");
Release();
return false;
}
}
for (int j = i + 1; j < allLines.Count; j++)
public void Release()
{
m_AStrTab.Clear();
m_WStrTab.Clear();
m_bInit = false;
m_bUnicode = false;
}
public string GetANSIString(int n) => m_AStrTab.TryGetValue(n, out var s) ? s : null;
public string GetWideString(int n) => m_WStrTab.TryGetValue(n, out var s) ? s : null;
public string GetWideStringObject(int n) => GetWideString(n);
public bool IsInitialized() => m_bInit;
// ==== Đọc từ Resources thay vì đường dẫn ====
protected bool LoadANSIStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
// Giải mã bytes -> string (ANSI: dùng Encoding.Default)
string content = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: false);
}
protected bool LoadWideStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
// Unity TextAsset mặc định đã decode text UTF8 -> textAsset.text
// nhưng để chắc chắn BOM/Unicode thì đọc từ bytes
string content;
content = ByteToStringUtils.ByteArrayToUnicodeString(textAsset.bytes);
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: true);
}
private static Encoding DetectEncoding(byte[] bom)
{
if (bom.Length >= 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) return Encoding.UTF8;
if (bom.Length >= 2 && bom[0] == 0xFF && bom[1] == 0xFE) return Encoding.Unicode;
if (bom.Length >= 2 && bom[0] == 0xFE && bom[1] == 0xFF) return Encoding.BigEndianUnicode;
return null;
}
private bool ParseIntoDict(StringReader sr, bool isWide)
{
bool bIndexMode = false;
bool bBegan = false;
int autoIndex = 0;
var allLines = new List<string>();
string line;
while ((line = sr.ReadLine()) != null)
{
allLines.Add(line);
}
for (int i = 0; i < allLines.Count; i++)
{
var ln = allLines[i].Trim();
if (ln.Length == 0) continue;
if (ln.Equals("#_index", StringComparison.OrdinalIgnoreCase))
{
var payload = allLines[j].Trim();
if (payload.Length == 0) continue;
if (payload.StartsWith("#")) continue;
if (payload.StartsWith("//")) continue;
if (bIndexMode)
{
if (!TrySplitIndexAndText(payload, out int idx, out string text))
continue;
PutString(idx, text, isWide);
}
else
{
PutString(autoIndex++, payload, isWide);
}
bIndexMode = true;
}
else if (ln.Equals("#_begin", StringComparison.OrdinalIgnoreCase))
{
bBegan = true;
for (int j = i + 1; j < allLines.Count; j++)
{
var payload = allLines[j].Trim();
if (payload.Length == 0) continue;
if (payload.StartsWith("#")) continue;
if (payload.StartsWith("//")) continue;
if (bIndexMode)
{
if (!TrySplitIndexAndText(payload, out int idx, out string text))
continue;
PutString(idx, text, isWide);
}
else
{
PutString(autoIndex++, payload, isWide);
}
}
break;
}
break;
}
return bBegan;
}
return bBegan;
}
private static bool TrySplitIndexAndText(string line, out int index, out string text)
{
index = 0; text = null;
int eq = line.IndexOf('=');
if (eq >= 0)
private static bool TrySplitIndexAndText(string line, out int index, out string text)
{
var left = line.Substring(0, eq).Trim();
var right = line.Substring(eq + 1);
if (int.TryParse(left, out index))
index = 0; text = null;
int eq = line.IndexOf('=');
if (eq >= 0)
{
text = right;
var left = line.Substring(0, eq).Trim();
var right = line.Substring(eq + 1);
if (int.TryParse(left, out index))
{
text = right;
return true;
}
return false;
}
int sp = FirstWhiteSpaceIndex(line);
if (sp <= 0) return false;
var left2 = line.Substring(0, sp).Trim();
var right2 = line.Substring(sp).TrimStart();
if (int.TryParse(left2, out index))
{
text = right2;
return true;
}
return false;
}
int sp = FirstWhiteSpaceIndex(line);
if (sp <= 0) return false;
var left2 = line.Substring(0, sp).Trim();
var right2 = line.Substring(sp).TrimStart();
if (int.TryParse(left2, out index))
private static int FirstWhiteSpaceIndex(string s)
{
text = right2;
return true;
for (int i = 0; i < s.Length; i++)
if (char.IsWhiteSpace(s[i])) return i;
return -1;
}
return false;
}
private static int FirstWhiteSpaceIndex(string s)
{
for (int i = 0; i < s.Length; i++)
if (char.IsWhiteSpace(s[i])) return i;
return -1;
private void PutString(int id, string value, bool isWide)
{
if (isWide) m_WStrTab[id] = value;
else m_AStrTab[id] = value;
}
}
private void PutString(int id, string value, bool isWide)
{
if (isWide) m_WStrTab[id] = value;
else m_AStrTab[id] = value;
}
}
}
File diff suppressed because one or more lines are too long