plan
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0c06c588e2a6442488a3542551fb243
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -5,6 +5,11 @@ namespace BrewMonster
|
||||
{
|
||||
public static partial class SkillStubs
|
||||
{
|
||||
public static void Init()
|
||||
{
|
||||
int i = 0;
|
||||
}
|
||||
|
||||
// Skill stub declarations
|
||||
public static Skill1Stub __stub_Skill1Stub = new Skill1Stub();
|
||||
public static Skill2Stub __stub_Skill2Stub = new Skill2Stub();
|
||||
|
||||
@@ -166,9 +166,6 @@ namespace BrewMonster
|
||||
public float GetAttackdistance(Skill skill) => 0f;
|
||||
public float GetAngle(Skill skill) => (float)(1 - 0.0111111 * 0);
|
||||
public override float GetPraydistance(Skill skill) => (float)(skill.GetPlayer().GetRange());
|
||||
public override int GetRequiredLevel(Skill skill) => RequiredLevelArray[skill.GetLevel() - 1];
|
||||
public override int GetRequiredSp(Skill skill) => RequiredSpArray[skill.GetLevel() - 1];
|
||||
public override int GetRequiredMoney(Skill skill) => RequiredMoneyArray[skill.GetLevel() - 1];
|
||||
|
||||
#if SKILL_CLIENT
|
||||
public override int GetIntroduction(Skill skill, StringBuilder buffer, string format)
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
#define SKILL_CLIENT
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using CSNetwork.GPDataType;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using static BrewMonster.PET_EVOLVE_CONFIG;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
|
||||
#if SKILL_SERVER
|
||||
public class Skill1 : Skill
|
||||
{
|
||||
public const int SKILL_ID = 1;
|
||||
|
||||
public Skill1() : base(SKILL_ID)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public class Skill1Stub : SkillStub
|
||||
{
|
||||
private static readonly int[] RequiredLevelArray = { 0, 5, 10, 15, 20, 25, 30, 35, 40, 45 };
|
||||
private static readonly int[] RequiredSpArray = { 0, 300, 1200, 2800, 5200, 8400, 12800, 18600, 26300, 36500 };
|
||||
private static readonly int[] RequiredMoneyArray = { 0, 30, 130, 280, 480, 730, 1180, 1630, 2080, 2580 };
|
||||
|
||||
|
||||
#if SKILL_SERVER
|
||||
public class State1 : SkillStub.State
|
||||
{
|
||||
public int GetTime(Skill skill) => 400;
|
||||
public bool Quit(Skill skill) => false;
|
||||
public bool Loop(Skill skill) => false;
|
||||
public bool Bypass(Skill skill) => false;
|
||||
public void Calculate(Skill skill)
|
||||
{
|
||||
skill.GetPlayer().SetDecmp(0.2f *(-5 + 7 * skill.GetLevel()));
|
||||
skill.GetPlayer().SetPray(1);
|
||||
}
|
||||
public bool Interrupt(Skill skill) => false;
|
||||
public bool Cancel(Skill skill) => true;
|
||||
public bool Skip(Skill skill) => false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SKILL_SERVER
|
||||
public class State2 : SkillStub.State
|
||||
{
|
||||
public int GetTime(Skill skill) => 700;
|
||||
public bool Quit(Skill skill) => false;
|
||||
public bool Loop(Skill skill) => false;
|
||||
public bool Bypass(Skill skill) => false;
|
||||
public void Calculate(Skill skill)
|
||||
{
|
||||
skill.GetPlayer().SetDecmp(0.8f *(-5 + 7 * skill.GetLevel()));
|
||||
skill.SetPlus(1.9f * skill.GetLevel() * skill.GetLevel() + 64 * skill.GetLevel() + 36.7f);
|
||||
skill.SetRatio(0);
|
||||
skill.SetDamage(skill.GetAttack());
|
||||
skill.GetPlayer().SetPerform(1);
|
||||
}
|
||||
public bool Interrupt(Skill skill) => false;
|
||||
public bool Cancel(Skill skill) => false;
|
||||
public bool Skip(Skill skill) => false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SKILL_SERVER
|
||||
public class State3 : SkillStub.State
|
||||
{
|
||||
public int GetTime(Skill skill) => 0;
|
||||
public bool Quit(Skill skill) => false;
|
||||
public bool Loop(Skill skill) => false;
|
||||
public bool Bypass(Skill skill) => false;
|
||||
public void Calculate(Skill skill)
|
||||
{
|
||||
}
|
||||
public bool Interrupt(Skill skill) => false;
|
||||
public bool Cancel(Skill skill) => false;
|
||||
public bool Skip(Skill skill) => false;
|
||||
}
|
||||
#endif
|
||||
|
||||
public Skill1Stub() : base(1)
|
||||
{
|
||||
cls = 0;
|
||||
name = "虎击";
|
||||
nativename = "虎击";
|
||||
icon = "虎击";
|
||||
max_level = 10;
|
||||
type = 1;
|
||||
apcost = 0;
|
||||
arrowcost = 0;
|
||||
apgain = 10;
|
||||
attr = 1;
|
||||
rank = 0;
|
||||
eventflag = 0;
|
||||
posdouble = 0;
|
||||
clslimit = 0;
|
||||
time_type = 0;
|
||||
showorder = 1101;
|
||||
allow_land = true;
|
||||
allow_air = true;
|
||||
allow_water = true;
|
||||
allow_ride = false;
|
||||
auto_attack = true;
|
||||
long_range = 0;
|
||||
restrict_corpse = 0;
|
||||
allow_forms = 1;
|
||||
effect = "虎击";
|
||||
doenchant = 0;
|
||||
dobless = 0;
|
||||
commoncooldown = 0;
|
||||
commoncooldowntime = 0;
|
||||
m_szFlyGfxPath = string.Empty;
|
||||
m_szHitGrndGfxPath = string.Empty;
|
||||
m_szHitGfxPath = "策划联入/人物技能/击中/虎击击中.gfx";
|
||||
|
||||
// GFX Movement and Behavior Parameters / GFX移动和行为参数
|
||||
m_MoveMode = (GfxMoveMode)0;
|
||||
m_TargetMode = (GfxTargetMode)0;
|
||||
m_AttFlyMode = (GfxAttackMode)0;
|
||||
m_AttHitMode = (GfxAttackMode)0;
|
||||
m_dwFlyTime = 0;
|
||||
m_bTraceTarget = false;
|
||||
m_FlyClusterCount = 1;
|
||||
m_FlyClusterInterval = 0;
|
||||
m_HitClusterCount = 1;
|
||||
m_HitClusterInterval = 0;
|
||||
m_bOneHit = true;
|
||||
m_bFadeOut = false;
|
||||
m_bRelScl = false;
|
||||
m_fDefTarScl = 1.8f;
|
||||
|
||||
// Area parameters (commented out) / 区域参数(已注释)
|
||||
// m_bArea = false;
|
||||
// m_Shape = (EmitShape)0;
|
||||
// m_vSize = new Vector3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Param (commented out) / 参数(已注释)
|
||||
// m_paramType = (GfxSkillValType)1;
|
||||
// m_param = new GFX_SKILL_PARAM();
|
||||
// m_param.nVal = 0;
|
||||
restrict_weapons.Add(0);
|
||||
restrict_weapons.Add(1);
|
||||
restrict_weapons.Add(182);
|
||||
restrict_weapons.Add(5);
|
||||
restrict_weapons.Add(292);
|
||||
restrict_weapons.Add(9);
|
||||
range = new Range();
|
||||
range.type = 0;
|
||||
#if SKILL_SERVER
|
||||
statestub.Add(new State1());
|
||||
statestub.Add(new State2());
|
||||
statestub.Add(new State3());
|
||||
#endif
|
||||
}
|
||||
|
||||
~Skill1Stub() { }
|
||||
|
||||
public override float GetMpcost(Skill skill) => (float)(-5 + 7 * skill.GetLevel());
|
||||
public override int GetExecutetime(Skill skill) => 700;
|
||||
public override int GetCoolingtime(Skill skill) => 3000;
|
||||
public float GetRadius(Skill skill) => 0f;
|
||||
public float GetAttackdistance(Skill skill) => 0f;
|
||||
public float GetAngle(Skill skill) => (float)(1 - 0.0111111 * 0);
|
||||
public override float GetPraydistance(Skill skill) => (float)(skill.GetPlayer().GetRange());
|
||||
public override int GetRequiredLevel(Skill skill) => RequiredLevelArray[skill.GetLevel() - 1];
|
||||
public override int GetRequiredSp(Skill skill) => RequiredSpArray[skill.GetLevel() - 1];
|
||||
public override int GetRequiredMoney(Skill skill) => RequiredMoneyArray[skill.GetLevel() - 1];
|
||||
|
||||
#if SKILL_CLIENT
|
||||
public override int GetIntroduction(Skill skill, StringBuilder buffer, string format)
|
||||
{
|
||||
buffer.Append(GPDataTypeHelper.ReplacePercentD(format,
|
||||
skill.GetLevel(),
|
||||
-5 + 7 * skill.GetLevel(),
|
||||
1.9 * skill.GetLevel() * skill.GetLevel() + 64 * skill.GetLevel() + 36.7));
|
||||
return buffer.Length;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SKILL_SERVER
|
||||
public int GetEnmity(Skill skill) => 0;
|
||||
public bool TakeEffect(Skill skill) => true;
|
||||
public float GetEffectdistance(Skill skill) => 13.3f;
|
||||
public int GetAttackspeed(Skill skill) => 3;
|
||||
public float GetHitrate(Skill skill) => 1.2f + 0.05f * skill.GetLevel();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ public partial class CECGameRun
|
||||
|
||||
private void LoadPrefabs()
|
||||
{
|
||||
BMLogger.LogError("CECGameRun::LoadPrefabs, Loading prefabs from Resources. Consider using Addressables for better performance and memory management.");
|
||||
_playerPrefab = Resources.Load<GameObject>(AddressResourceConfig.PlayerPrefab);
|
||||
_monsterPrefab = Resources.Load<GameObject>(AddressResourceConfig.MonsterPrefab);
|
||||
_npcServerPrefab = Resources.Load<GameObject>(AddressResourceConfig.NpcServerPrefab);
|
||||
|
||||
+224
-138
@@ -52,7 +52,7 @@ A3DSkillGfxMan.AddSkillGfxEvent() - Creates GFX events with clustering ✅
|
||||
↓
|
||||
A3DSkillGfxEvent.Tick() - State machine (Wait → Flying → Hit → Finished) ⚠️ COMMENTED OUT
|
||||
↓
|
||||
CGfxMoveBase / IGfxMovement - Movement calculation ❌ NOT CREATED
|
||||
CGfxMoveBase - Movement calculation ❌ NOT CREATED
|
||||
↓
|
||||
GFX Spawning (fly/hit) - Instantiate prefabs ❌ NOT IMPLEMENTED
|
||||
```
|
||||
@@ -169,7 +169,7 @@ GFX Spawning (fly/hit) - Instantiate prefabs ❌ NOT IMPLEMENTED
|
||||
### 2.3 What's Completely Missing ❌
|
||||
|
||||
1. **Movement System (CGfxMoveBase)**
|
||||
- No `IGfxMovement` interface
|
||||
- No `CGfxMoveBase` abstract class
|
||||
- No movement implementations (Linear, Parabolic, Missile, etc.)
|
||||
- `m_pMoveMethod` referenced in comments but never created
|
||||
- All movement-related calls are commented out
|
||||
@@ -278,140 +278,226 @@ event.Tick() → mostly no-op event.Tick() → full state machine:
|
||||
|
||||
**New Files:**
|
||||
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/IGfxMovement.cs`**
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/CGfxMoveBase.cs`** (same name as C++)
|
||||
```csharp
|
||||
public interface IGfxMovement
|
||||
// Mirrors C++ CGfxMoveBase exactly:
|
||||
// - StartMove / TickMove are pure virtual (= 0) → abstract
|
||||
// - SetParam / SetIsCluster / SetReverse / UpdateGfxParam are virtual with default impl → virtual
|
||||
// - GetMode / GetPos / GetMoveDir / IsReverse / SetMaxFlyTime are non-virtual → regular methods
|
||||
public abstract class CGfxMoveBase
|
||||
{
|
||||
void SetMaxFlyTime(uint dwMaxFlyTime);
|
||||
void StartMove(Vector3 vHost, Vector3 vTarget);
|
||||
bool TickMove(float deltaTimeMs, Vector3 vHostPos, Vector3 vTargetPos);
|
||||
Vector3 GetPos();
|
||||
Vector3 GetMoveDir();
|
||||
GfxMoveMode GetMode();
|
||||
GfxHitPos GetHitPos();
|
||||
bool IsReverse();
|
||||
void SetReverse(bool bReverse);
|
||||
void SetParam(GFX_SKILL_PARAM param);
|
||||
void SetIsCluster(bool bCluster);
|
||||
// Fields (same as C++)
|
||||
protected GfxMoveMode m_Mode;
|
||||
protected GfxHitPos m_HitPos = GfxHitPos.enumHitCenter;
|
||||
protected Vector3 m_vPos;
|
||||
protected Vector3 m_vMoveDir;
|
||||
protected bool m_bOneOfCluser; // C++ spelling kept
|
||||
protected uint m_dwMaxFlyTime;
|
||||
protected bool m_bReverse;
|
||||
protected bool m_bArea;
|
||||
protected EmitShape m_Shape;
|
||||
protected Vector3 m_vSize;
|
||||
protected Vector3 m_vXRange;
|
||||
protected Vector3 m_vYRange;
|
||||
protected Vector3 m_vZRange;
|
||||
protected float m_fSquare;
|
||||
protected float m_fSquareH;
|
||||
|
||||
protected CGfxMoveBase(GfxMoveMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
m_HitPos = GfxHitPos.enumHitCenter;
|
||||
}
|
||||
|
||||
// Pure virtual (= 0) → abstract — subclasses MUST override
|
||||
public abstract void StartMove(Vector3 vHost, Vector3 vTarget);
|
||||
public abstract bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos);
|
||||
|
||||
// Protected helpers (same as C++ CGfxMoveBase)
|
||||
protected static float Normalize(ref Vector3 v)
|
||||
{
|
||||
float mag = v.magnitude;
|
||||
if (mag < 1e-6f) { v = Vector3.zero; return 0f; }
|
||||
v /= mag;
|
||||
return mag;
|
||||
}
|
||||
|
||||
protected void CalcRange(Vector3 vDir)
|
||||
{
|
||||
m_vYRange = Vector3.up;
|
||||
m_vZRange = new Vector3(vDir.x, 0, vDir.z);
|
||||
if (Normalize(ref m_vZRange) < 0.01f) m_vZRange = Vector3.forward;
|
||||
m_vXRange = Vector3.Cross(m_vYRange, m_vZRange);
|
||||
m_vXRange *= m_vSize.x;
|
||||
m_vYRange *= m_vSize.y;
|
||||
m_vZRange *= m_vSize.z;
|
||||
m_fSquare = m_vSize.sqrMagnitude;
|
||||
m_fSquareH = m_vSize.x * m_vSize.x + m_vSize.z * m_vSize.z;
|
||||
}
|
||||
|
||||
protected Vector3 GetRandOff()
|
||||
{
|
||||
// Mirrors C++ GetRandOff() with enumBox/enumSphere/enumCylinder
|
||||
float x, y, z;
|
||||
if (m_Shape == EmitShape.enumBox)
|
||||
return SymRandom() * m_vXRange + SymRandom() * m_vYRange + SymRandom() * m_vZRange;
|
||||
else if (m_Shape == EmitShape.enumSphere)
|
||||
{
|
||||
do { x = SymRandom(); y = SymRandom(); z = SymRandom(); }
|
||||
while (x*x + y*y + z*z > 1f);
|
||||
return x * m_vXRange + y * m_vYRange + z * m_vZRange;
|
||||
}
|
||||
else if (m_Shape == EmitShape.enumCylinder)
|
||||
{
|
||||
y = SymRandom();
|
||||
do { x = SymRandom(); z = SymRandom(); }
|
||||
while (x*x + z*z > 1f);
|
||||
return x * m_vXRange + y * m_vYRange + z * m_vZRange;
|
||||
}
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
private static float SymRandom() { return UnityEngine.Random.value * 2f - 1f; }
|
||||
|
||||
// Virtual with default implementation — subclasses CAN override, but don't have to
|
||||
public virtual void SetParam(GFX_SKILL_PARAM param)
|
||||
{
|
||||
m_bArea = param.m_bArea;
|
||||
m_Shape = param.m_Shape;
|
||||
m_vSize = new Vector3(param.m_vSize.x, param.m_vSize.y, param.m_vSize.z);
|
||||
}
|
||||
public virtual void SetIsCluster(bool bCluster) { m_bOneOfCluser = bCluster; }
|
||||
public virtual void SetReverse(bool bReverse) { m_bReverse = bReverse; }
|
||||
public virtual void UpdateGfxParam(Vector3 vHost, Vector3 vTarget) { }
|
||||
|
||||
// Non-virtual — same as C++ (no override possible)
|
||||
public GfxMoveMode GetMode() { return m_Mode; }
|
||||
public GfxHitPos GetHitPos() { return m_HitPos; }
|
||||
public Vector3 GetPos() { return m_vPos; }
|
||||
public Vector3 GetMoveDir() { return m_vMoveDir; }
|
||||
public bool IsReverse() { return m_bReverse; }
|
||||
public void SetMaxFlyTime(uint dwTime) { m_dwMaxFlyTime = dwTime; if (m_dwMaxFlyTime == 0) m_dwMaxFlyTime = 1; }
|
||||
public void SetIsArea(bool bArea) { m_bArea = bArea; }
|
||||
public void SetShape(EmitShape shape) { m_Shape = shape; }
|
||||
public void SetRange(Vector3 vSize) { m_vSize = vSize; }
|
||||
|
||||
// Factory method (same as C++ CGfxMoveBase::CreateMoveMethod)
|
||||
public static CGfxMoveBase CreateMoveMethod(GfxMoveMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GfxMoveMode.enumLinearMove: return new CGfxLinearMove(mode);
|
||||
case GfxMoveMode.enumOnTarget: return new CGfxOnTargetMove(mode);
|
||||
// Phase 2: add more modes
|
||||
default: return new CGfxOnTargetMove(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/GfxLinearMove.cs`** (C++ ref: A3DSkillGfxEvent2.cpp:22-52)
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/CGfxLinearMove.cs`** (C++ ref: A3DSkillGfxEvent2.cpp:22-52)
|
||||
```csharp
|
||||
public class GfxLinearMove : IGfxMovement
|
||||
{
|
||||
private Vector3 m_vPos;
|
||||
private Vector3 m_vMoveDir;
|
||||
private float m_fSpeed;
|
||||
private uint m_dwMaxFlyTime;
|
||||
private bool m_bReverse;
|
||||
|
||||
private const float _fly_speed = 20.0f; // units/sec
|
||||
|
||||
public void SetMaxFlyTime(uint dwMaxFlyTime) { m_dwMaxFlyTime = dwMaxFlyTime; }
|
||||
|
||||
public void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
m_vPos = vHost;
|
||||
m_vMoveDir = vTarget - m_vPos;
|
||||
float fDist = m_vMoveDir.magnitude;
|
||||
if (fDist < 1e-4f) { m_vMoveDir = Vector3.forward; m_fSpeed = _fly_speed; return; }
|
||||
m_vMoveDir /= fDist;
|
||||
|
||||
float fMax = _fly_speed / 1000f * m_dwMaxFlyTime;
|
||||
m_fSpeed = fMax >= fDist ? _fly_speed / 1000f : fDist / m_dwMaxFlyTime;
|
||||
}
|
||||
|
||||
public bool TickMove(float deltaTimeMs, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
{
|
||||
Vector3 vFlyDir = vTargetPos - m_vPos;
|
||||
float fDist = vFlyDir.magnitude;
|
||||
if (fDist < 1e-4f) return true;
|
||||
vFlyDir /= fDist;
|
||||
|
||||
float fFlyDist = m_fSpeed * deltaTimeMs;
|
||||
if (fFlyDist >= fDist) return true;
|
||||
|
||||
m_vPos += vFlyDir * fFlyDist;
|
||||
m_vMoveDir = vFlyDir;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Vector3 GetPos() => m_vPos;
|
||||
public Vector3 GetMoveDir() => m_vMoveDir;
|
||||
public GfxMoveMode GetMode() => GfxMoveMode.enumLinearMove;
|
||||
public GfxHitPos GetHitPos() => GfxHitPos.enumHitCenter;
|
||||
public bool IsReverse() => m_bReverse;
|
||||
public void SetReverse(bool b) { m_bReverse = b; }
|
||||
public void SetParam(GFX_SKILL_PARAM param) { }
|
||||
public void SetIsCluster(bool b) { }
|
||||
}
|
||||
```
|
||||
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/GfxOnTargetMove.cs`** (for instant-hit skills like 虎击)
|
||||
```csharp
|
||||
public class GfxOnTargetMove : IGfxMovement
|
||||
{
|
||||
private Vector3 m_vPos;
|
||||
private Vector3 m_vMoveDir;
|
||||
private bool m_bReverse;
|
||||
|
||||
public void SetMaxFlyTime(uint dwMaxFlyTime) { }
|
||||
|
||||
public void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
m_vPos = vTarget; // Instantly at target
|
||||
m_vMoveDir = (vTarget - vHost);
|
||||
if (m_vMoveDir.magnitude > 1e-4f) m_vMoveDir.Normalize();
|
||||
else m_vMoveDir = Vector3.forward;
|
||||
}
|
||||
|
||||
public bool TickMove(float deltaTimeMs, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
{
|
||||
m_vPos = vTargetPos;
|
||||
return true; // Instantly hits
|
||||
}
|
||||
|
||||
public Vector3 GetPos() => m_vPos;
|
||||
public Vector3 GetMoveDir() => m_vMoveDir;
|
||||
public GfxMoveMode GetMode() => GfxMoveMode.enumOnTarget;
|
||||
public GfxHitPos GetHitPos() => GfxHitPos.enumHitCenter;
|
||||
public bool IsReverse() => m_bReverse;
|
||||
public void SetReverse(bool b) { m_bReverse = b; }
|
||||
public void SetParam(GFX_SKILL_PARAM param) { }
|
||||
public void SetIsCluster(bool b) { }
|
||||
}
|
||||
```
|
||||
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/GfxMoveFactory.cs`**
|
||||
```csharp
|
||||
public static class GfxMoveFactory
|
||||
// Mirrors C++ CGfxLinearMove exactly (A3DSkillGfxEvent2.cpp:22-52)
|
||||
public class CGfxLinearMove : CGfxMoveBase
|
||||
{
|
||||
public static IGfxMovement Create(GfxMoveMode mode)
|
||||
protected float m_fSpeed;
|
||||
private const float _fly_speed = 20.0f / 1000.0f; // same as C++
|
||||
|
||||
public CGfxLinearMove(GfxMoveMode mode) : base(mode) { }
|
||||
|
||||
public override void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
switch (mode)
|
||||
if (m_bArea)
|
||||
{
|
||||
case GfxMoveMode.enumLinearMove: return new GfxLinearMove();
|
||||
case GfxMoveMode.enumOnTarget: return new GfxOnTargetMove();
|
||||
// Phase 2: add more modes
|
||||
default: return new GfxLinearMove();
|
||||
CalcRange((vTarget - vHost).normalized);
|
||||
m_vPos = vHost + GetRandOff();
|
||||
}
|
||||
else
|
||||
m_vPos = vHost;
|
||||
|
||||
m_vMoveDir = vTarget - m_vPos;
|
||||
float fDist = Normalize(ref m_vMoveDir);
|
||||
float fMax = _fly_speed * m_dwMaxFlyTime;
|
||||
|
||||
if (fMax >= fDist)
|
||||
m_fSpeed = _fly_speed;
|
||||
else
|
||||
m_fSpeed = fDist / m_dwMaxFlyTime;
|
||||
}
|
||||
|
||||
public override bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
{
|
||||
Vector3 vFlyDir = vTargetPos - m_vPos;
|
||||
float fDist = Normalize(ref vFlyDir);
|
||||
float fFlyDist = m_fSpeed * dwDeltaTime;
|
||||
|
||||
if (fFlyDist >= fDist) return true; // target hit
|
||||
m_vPos += vFlyDir * fFlyDist;
|
||||
m_vMoveDir = vFlyDir;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`Assets/PerfectWorld/Scripts/Vfx/CGfxOnTargetMove.cs`** (for instant-hit skills like 虎击)
|
||||
```csharp
|
||||
// Mirrors C++ CGfxOnTargetMove exactly (A3DSkillGfxEvent2.cpp:297-322)
|
||||
public class CGfxOnTargetMove : CGfxMoveBase
|
||||
{
|
||||
protected float m_fRadius;
|
||||
protected Vector3 m_vOffset;
|
||||
|
||||
public CGfxOnTargetMove(GfxMoveMode mode) : base(mode) { m_HitPos = GfxHitPos.enumHitBottom; m_fRadius = 0; }
|
||||
|
||||
public override void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
m_vPos = vTarget;
|
||||
m_vMoveDir = vTarget - vHost;
|
||||
m_vMoveDir.y = 0; // C++: zero out Y before normalize
|
||||
if (Normalize(ref m_vMoveDir) == 0)
|
||||
m_vMoveDir = Vector3.forward; // _unit_z
|
||||
|
||||
if (m_bOneOfCluser)
|
||||
{
|
||||
float fRandAng = UnityEngine.Random.value * Mathf.PI * 2f;
|
||||
float fRadius = UnityEngine.Random.value * m_fRadius;
|
||||
m_vOffset.x = Mathf.Cos(fRandAng) * fRadius;
|
||||
m_vOffset.z = Mathf.Sin(fRandAng) * fRadius;
|
||||
m_vOffset.y = 0;
|
||||
m_vPos += m_vOffset;
|
||||
}
|
||||
else
|
||||
m_vOffset = Vector3.zero;
|
||||
}
|
||||
|
||||
public override bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
{
|
||||
m_vPos = vTargetPos + m_vOffset;
|
||||
return false; // C++ returns false — hit is triggered by fly time timeout, NOT by TickMove
|
||||
}
|
||||
|
||||
public override void SetParam(GFX_SKILL_PARAM param)
|
||||
{
|
||||
base.SetParam(param);
|
||||
m_fRadius = param.value.fVal; // C# union access: param.value.fVal
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*(No separate factory file needed — `CGfxMoveBase.CreateMoveMethod()` handles creation, same as C++)*
|
||||
|
||||
#### Task 1.2: Un-comment & Wire Up Event State Machine (1-2 days)
|
||||
|
||||
**Changes to `A3DSkillGfxMan.cs` — `A3DSkillGfxEvent` class:**
|
||||
|
||||
1. Add `m_pMoveMethod` field (un-comment):
|
||||
```csharp
|
||||
protected IGfxMovement m_pMoveMethod;
|
||||
protected CGfxMoveBase m_pMoveMethod;
|
||||
```
|
||||
|
||||
2. In constructor, create movement:
|
||||
```csharp
|
||||
m_pMoveMethod = GfxMoveFactory.Create(mode);
|
||||
m_pMoveMethod = CGfxMoveBase.CreateMoveMethod(mode);
|
||||
```
|
||||
|
||||
3. Un-comment the state machine in `Tick()`:
|
||||
@@ -491,18 +577,18 @@ public static class GfxMoveFactory
|
||||
|
||||
| Mode | Class | C++ Ref | Complexity | Priority |
|
||||
|------|-------|---------|------------|----------|
|
||||
| Linear | `GfxLinearMove` | lines 22-52 | ✅ Done in Phase 1 | — |
|
||||
| OnTarget | `GfxOnTargetMove` | — | ✅ Done in Phase 1 | — |
|
||||
| Parabolic | `GfxParabolicMove` | lines 54-92 | Medium | High |
|
||||
| Missile | `GfxMissileMove` | lines 94-142 | High | High |
|
||||
| Meteoric | `GfxMeteoricMove` | lines 144-188 | Low | Medium |
|
||||
| Accelerated | `GfxAccMove` | lines 319-370 | Low | Medium |
|
||||
| Helix | `GfxHelixMove` | lines 190-256 | Medium | Low |
|
||||
| Curved | `GfxCurvedMove` | lines 258-317 | High | Low |
|
||||
| Link | `GfxLinkMove` | lines 372-420 | Medium | Low |
|
||||
| Random | `GfxRandMove` | lines 422-458 | Medium | Low |
|
||||
| Linear | `CGfxLinearMove` | lines 22-52 | ✅ Done in Phase 1 | — |
|
||||
| OnTarget | `CGfxOnTargetMove` | — | ✅ Done in Phase 1 | — |
|
||||
| Parabolic | `CGfxParabolicMove` | lines 54-92 | Medium | High |
|
||||
| Missile | `CGfxMissileMove` | lines 94-142 | High | High |
|
||||
| Meteoric | `CGfxMeteoricMove` | lines 144-188 | Low | Medium |
|
||||
| Accelerated | `CGfxAccMove` | lines 319-370 | Low | Medium |
|
||||
| Helix | `CGfxHelixMove` | lines 190-256 | Medium | Low |
|
||||
| Curved | `CGfxCurvedMove` | lines 258-317 | High | Low |
|
||||
| Link | `CGfxLinkMove` | lines 372-420 | Medium | Low |
|
||||
| Random | `CGfxRandMove` | lines 422-458 | Medium | Low |
|
||||
|
||||
**Pattern:** Each implements `IGfxMovement`, added to `GfxMoveFactory.Create()`
|
||||
**Pattern:** Each extends `CGfxMoveBase`, added to `CGfxMoveBase.CreateMoveMethod()`
|
||||
|
||||
#### Task 2.2: GFX Lifecycle Management (2-3 days)
|
||||
|
||||
@@ -676,7 +762,7 @@ Complete the NPC melee branch in `DoFire()`:
|
||||
|
||||
| File | Lines | What Needs Changing |
|
||||
|------|-------|---------------------|
|
||||
| `A3DSkillGfxMan.cs` | 516 | Un-comment movement + state machine in Tick(), add IGfxMovement field, remove SpawnGFX call |
|
||||
| `A3DSkillGfxMan.cs` | 516 | Un-comment movement + state machine in Tick(), add CGfxMoveBase field, remove SpawnGFX call |
|
||||
| `CECSkillGfxMan.cs` | 878 | Un-comment Tick() position updates, implement GFX spawning, un-comment HitTarget() |
|
||||
| `CECAttacksMan.cs` | 1354 | Fix Play() GFX strings, add scale fields, un-comment NPC skill branch, remove SpawnGFX hack |
|
||||
|
||||
@@ -684,18 +770,18 @@ Complete the NPC melee branch in `DoFire()`:
|
||||
|
||||
| File | Phase | Purpose |
|
||||
|------|-------|---------|
|
||||
| `IGfxMovement.cs` | Phase 1 | Movement interface |
|
||||
| `GfxLinearMove.cs` | Phase 1 | Straight-line projectile |
|
||||
| `GfxOnTargetMove.cs` | Phase 1 | Instant-hit (no flight) |
|
||||
| `GfxMoveFactory.cs` | Phase 1 | Factory for movement modes |
|
||||
| `GfxParabolicMove.cs` | Phase 2 | Arc trajectory |
|
||||
| `GfxMissileMove.cs` | Phase 2 | Homing missile |
|
||||
| `GfxMeteoricMove.cs` | Phase 2 | Falls from sky |
|
||||
| `GfxAccMove.cs` | Phase 2 | Accelerated flight |
|
||||
| `GfxHelixMove.cs` | Phase 2 | Spiral path |
|
||||
| `GfxCurvedMove.cs` | Phase 2 | Bezier curve |
|
||||
| `GfxLinkMove.cs` | Phase 2 | Lightning chain |
|
||||
| `GfxRandMove.cs` | Phase 2 | Random walk |
|
||||
| `CGfxMoveBase.cs` | Phase 1 | Movement abstract base class (same as C++) |
|
||||
| `CGfxLinearMove.cs` | Phase 1 | Straight-line projectile |
|
||||
| `CGfxOnTargetMove.cs` | Phase 1 | Instant-hit (no flight) |
|
||||
| *(no separate factory file)* | — | *Factory is `CGfxMoveBase.CreateMoveMethod()`, same as C++* |
|
||||
| `CGfxParabolicMove.cs` | Phase 2 | Arc trajectory |
|
||||
| `CGfxMissileMove.cs` | Phase 2 | Homing missile |
|
||||
| `CGfxMeteoricMove.cs` | Phase 2 | Falls from sky |
|
||||
| `CGfxAccMove.cs` | Phase 2 | Accelerated flight |
|
||||
| `CGfxHelixMove.cs` | Phase 2 | Spiral path |
|
||||
| `CGfxCurvedMove.cs` | Phase 2 | Bezier curve |
|
||||
| `CGfxLinkMove.cs` | Phase 2 | Lightning chain |
|
||||
| `CGfxRandMove.cs` | Phase 2 | Random walk |
|
||||
| `GfxPool.cs` | Phase 2 | Object pooling |
|
||||
|
||||
---
|
||||
@@ -822,8 +908,8 @@ BMLogger.Log($"[GFX_FLOW] HitTarget at {vTarget}");
|
||||
|
||||
| Line(s) | What | Action |
|
||||
|---------|------|--------|
|
||||
| 205-206 | `m_pMoveMethod` field | Un-comment, use IGfxMovement |
|
||||
| 251 | `m_pMoveMethod = CGfxMoveBase.CreateMoveMethod(mode)` | Replace with `GfxMoveFactory.Create(mode)` |
|
||||
| 205-206 | `m_pMoveMethod` field | Un-comment, use CGfxMoveBase |
|
||||
| 251 | `m_pMoveMethod = CGfxMoveBase.CreateMoveMethod(mode)` | Un-comment (same name as C++) |
|
||||
| 336-337 | `GetMoveMethod()`, `GetMode()`, `GetHitPos()` | Un-comment |
|
||||
| 343 | `SetReverse()` | Un-comment |
|
||||
| 344-345 | `SetParam()`, `SetIsCluster()` | Un-comment |
|
||||
|
||||
+208
-111
@@ -9,7 +9,7 @@
|
||||
- ⚠️ **State machine commented out** — Tick() logic exists but is commented out
|
||||
- ⚠️ **GFX strings empty** — Play() passes `""` instead of actual GFX names
|
||||
- ⚠️ **SpawnGFX temp hack** — Instantiates flyGFX at target pos (wrong, needs removal)
|
||||
- ❌ **Movement system missing** — No IGfxMovement, no GfxLinearMove, etc.
|
||||
- ❌ **Movement system missing** — No CGfxMoveBase, no CGfxLinearMove, etc.
|
||||
- ❌ **GFX spawning missing** — No fly/hit GFX instantiation in event Tick()
|
||||
|
||||
### What's the Skill GFX System?
|
||||
@@ -37,7 +37,7 @@ SpawnGFX() temp hack ⚠️ WRONG - needs removal
|
||||
↓
|
||||
A3DSkillGfxEvent.Tick() ⚠️ State machine COMMENTED OUT
|
||||
↓
|
||||
IGfxMovement.TickMove() ❌ NOT CREATED
|
||||
CGfxMoveBase.TickMove() ❌ NOT CREATED
|
||||
↓
|
||||
Spawn/update fly GFX ❌ NOT IMPLEMENTED
|
||||
↓
|
||||
@@ -52,142 +52,239 @@ CECAttackEvent.DoDamage() ✅ Working
|
||||
|
||||
### Step 1: Create Movement System (1 day)
|
||||
|
||||
Create 4 new files in `Assets/PerfectWorld/Scripts/Vfx/`:
|
||||
Create 3 new files in `Assets/PerfectWorld/Scripts/Vfx/`:
|
||||
|
||||
**`IGfxMovement.cs`** — Movement interface
|
||||
```csharp
|
||||
namespace BrewMonster
|
||||
{
|
||||
public interface IGfxMovement
|
||||
{
|
||||
void SetMaxFlyTime(uint dwMaxFlyTime);
|
||||
void StartMove(UnityEngine.Vector3 vHost, UnityEngine.Vector3 vTarget);
|
||||
bool TickMove(float deltaTimeMs, UnityEngine.Vector3 vHostPos, UnityEngine.Vector3 vTargetPos);
|
||||
UnityEngine.Vector3 GetPos();
|
||||
UnityEngine.Vector3 GetMoveDir();
|
||||
GfxMoveMode GetMode();
|
||||
GfxHitPos GetHitPos();
|
||||
bool IsReverse();
|
||||
void SetReverse(bool bReverse);
|
||||
void SetParam(GFX_SKILL_PARAM param);
|
||||
void SetIsCluster(bool bCluster);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`GfxLinearMove.cs`** — Straight-line projectile
|
||||
**`CGfxMoveBase.cs`** — Movement base class (same name and pattern as C++)
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class GfxLinearMove : IGfxMovement
|
||||
// Mirrors C++ CGfxMoveBase exactly:
|
||||
// - StartMove / TickMove → pure virtual (= 0) → abstract
|
||||
// - SetParam / SetIsCluster / SetReverse → virtual with default impl → virtual
|
||||
// - GetMode / GetPos / GetMoveDir / IsReverse / SetMaxFlyTime → non-virtual → regular
|
||||
public abstract class CGfxMoveBase
|
||||
{
|
||||
private Vector3 m_vPos, m_vMoveDir;
|
||||
private float m_fSpeed;
|
||||
private uint m_dwMaxFlyTime;
|
||||
private bool m_bReverse;
|
||||
private const float _fly_speed = 20.0f; // units/sec → 0.02 units/ms
|
||||
protected GfxMoveMode m_Mode;
|
||||
protected GfxHitPos m_HitPos = GfxHitPos.enumHitCenter;
|
||||
protected Vector3 m_vPos;
|
||||
protected Vector3 m_vMoveDir;
|
||||
protected bool m_bOneOfCluser; // C++ spelling kept
|
||||
protected uint m_dwMaxFlyTime;
|
||||
protected bool m_bReverse;
|
||||
protected bool m_bArea;
|
||||
protected EmitShape m_Shape;
|
||||
protected Vector3 m_vSize;
|
||||
protected Vector3 m_vXRange;
|
||||
protected Vector3 m_vYRange;
|
||||
protected Vector3 m_vZRange;
|
||||
protected float m_fSquare;
|
||||
protected float m_fSquareH;
|
||||
|
||||
public void SetMaxFlyTime(uint t) { m_dwMaxFlyTime = t; }
|
||||
|
||||
public void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
protected CGfxMoveBase(GfxMoveMode mode)
|
||||
{
|
||||
m_vPos = vHost;
|
||||
m_vMoveDir = vTarget - vHost;
|
||||
float fDist = m_vMoveDir.magnitude;
|
||||
if (fDist < 1e-4f) { m_vMoveDir = Vector3.forward; m_fSpeed = _fly_speed / 1000f; return; }
|
||||
m_vMoveDir /= fDist;
|
||||
float fMax = (_fly_speed / 1000f) * m_dwMaxFlyTime;
|
||||
m_fSpeed = fMax >= fDist ? _fly_speed / 1000f : fDist / m_dwMaxFlyTime;
|
||||
m_Mode = mode;
|
||||
m_HitPos = GfxHitPos.enumHitCenter;
|
||||
}
|
||||
|
||||
public bool TickMove(float dtMs, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
// Pure virtual (= 0) → abstract — subclasses MUST override
|
||||
public abstract void StartMove(Vector3 vHost, Vector3 vTarget);
|
||||
public abstract bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos);
|
||||
|
||||
// Protected helpers (same as C++ CGfxMoveBase)
|
||||
protected static float Normalize(ref Vector3 v)
|
||||
{
|
||||
Vector3 dir = vTargetPos - m_vPos;
|
||||
float dist = dir.magnitude;
|
||||
if (dist < 1e-4f) return true;
|
||||
dir /= dist;
|
||||
float fly = m_fSpeed * dtMs;
|
||||
if (fly >= dist) return true;
|
||||
m_vPos += dir * fly;
|
||||
m_vMoveDir = dir;
|
||||
return false;
|
||||
float mag = v.magnitude;
|
||||
if (mag < 1e-6f) { v = Vector3.zero; return 0f; }
|
||||
v /= mag;
|
||||
return mag;
|
||||
}
|
||||
|
||||
public Vector3 GetPos() => m_vPos;
|
||||
public Vector3 GetMoveDir() => m_vMoveDir;
|
||||
public GfxMoveMode GetMode() => GfxMoveMode.enumLinearMove;
|
||||
public GfxHitPos GetHitPos() => GfxHitPos.enumHitCenter;
|
||||
public bool IsReverse() => m_bReverse;
|
||||
public void SetReverse(bool b) { m_bReverse = b; }
|
||||
public void SetParam(GFX_SKILL_PARAM p) { }
|
||||
public void SetIsCluster(bool b) { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`GfxOnTargetMove.cs`** — Instant hit (no flight)
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class GfxOnTargetMove : IGfxMovement
|
||||
{
|
||||
private Vector3 m_vPos, m_vMoveDir;
|
||||
private bool m_bReverse;
|
||||
|
||||
public void SetMaxFlyTime(uint t) { }
|
||||
public void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
protected void CalcRange(Vector3 vDir)
|
||||
{
|
||||
m_vPos = vTarget;
|
||||
m_vMoveDir = (vTarget - vHost);
|
||||
if (m_vMoveDir.magnitude > 1e-4f) m_vMoveDir.Normalize();
|
||||
else m_vMoveDir = Vector3.forward;
|
||||
m_vYRange = Vector3.up;
|
||||
m_vZRange = new Vector3(vDir.x, 0, vDir.z);
|
||||
if (Normalize(ref m_vZRange) < 0.01f) m_vZRange = Vector3.forward;
|
||||
m_vXRange = Vector3.Cross(m_vYRange, m_vZRange);
|
||||
m_vXRange *= m_vSize.x;
|
||||
m_vYRange *= m_vSize.y;
|
||||
m_vZRange *= m_vSize.z;
|
||||
m_fSquare = m_vSize.sqrMagnitude;
|
||||
m_fSquareH = m_vSize.x * m_vSize.x + m_vSize.z * m_vSize.z;
|
||||
}
|
||||
public bool TickMove(float dtMs, Vector3 h, Vector3 t) { m_vPos = t; return true; }
|
||||
public Vector3 GetPos() => m_vPos;
|
||||
public Vector3 GetMoveDir() => m_vMoveDir;
|
||||
public GfxMoveMode GetMode() => GfxMoveMode.enumOnTarget;
|
||||
public GfxHitPos GetHitPos() => GfxHitPos.enumHitCenter;
|
||||
public bool IsReverse() => m_bReverse;
|
||||
public void SetReverse(bool b) { m_bReverse = b; }
|
||||
public void SetParam(GFX_SKILL_PARAM p) { }
|
||||
public void SetIsCluster(bool b) { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`GfxMoveFactory.cs`** — Factory
|
||||
```csharp
|
||||
namespace BrewMonster
|
||||
{
|
||||
public static class GfxMoveFactory
|
||||
{
|
||||
public static IGfxMovement Create(GfxMoveMode mode)
|
||||
protected Vector3 GetRandOff()
|
||||
{
|
||||
float x, y, z;
|
||||
if (m_Shape == EmitShape.enumBox)
|
||||
return SymRandom() * m_vXRange + SymRandom() * m_vYRange + SymRandom() * m_vZRange;
|
||||
else if (m_Shape == EmitShape.enumSphere)
|
||||
{
|
||||
do { x = SymRandom(); y = SymRandom(); z = SymRandom(); }
|
||||
while (x*x + y*y + z*z > 1f);
|
||||
return x * m_vXRange + y * m_vYRange + z * m_vZRange;
|
||||
}
|
||||
else if (m_Shape == EmitShape.enumCylinder)
|
||||
{
|
||||
y = SymRandom();
|
||||
do { x = SymRandom(); z = SymRandom(); }
|
||||
while (x*x + z*z > 1f);
|
||||
return x * m_vXRange + y * m_vYRange + z * m_vZRange;
|
||||
}
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
private static float SymRandom() { return UnityEngine.Random.value * 2f - 1f; }
|
||||
|
||||
// Virtual with default implementation — subclasses CAN override
|
||||
public virtual void SetParam(GFX_SKILL_PARAM param)
|
||||
{
|
||||
m_bArea = param.m_bArea;
|
||||
m_Shape = param.m_Shape;
|
||||
m_vSize = new Vector3(param.m_vSize.x, param.m_vSize.y, param.m_vSize.z);
|
||||
}
|
||||
public virtual void SetIsCluster(bool bCluster) { m_bOneOfCluser = bCluster; }
|
||||
public virtual void SetReverse(bool bReverse) { m_bReverse = bReverse; }
|
||||
public virtual void UpdateGfxParam(Vector3 vHost, Vector3 vTarget) { }
|
||||
|
||||
// Non-virtual — same as C++
|
||||
public GfxMoveMode GetMode() { return m_Mode; }
|
||||
public GfxHitPos GetHitPos() { return m_HitPos; }
|
||||
public Vector3 GetPos() { return m_vPos; }
|
||||
public Vector3 GetMoveDir() { return m_vMoveDir; }
|
||||
public bool IsReverse() { return m_bReverse; }
|
||||
public void SetMaxFlyTime(uint dwTime)
|
||||
{
|
||||
m_dwMaxFlyTime = dwTime;
|
||||
if (m_dwMaxFlyTime == 0) m_dwMaxFlyTime = 1;
|
||||
}
|
||||
public void SetIsArea(bool bArea) { m_bArea = bArea; }
|
||||
public void SetShape(EmitShape shape) { m_Shape = shape; }
|
||||
public void SetRange(Vector3 vSize) { m_vSize = vSize; }
|
||||
|
||||
// Factory method (same as C++ CGfxMoveBase::CreateMoveMethod)
|
||||
public static CGfxMoveBase CreateMoveMethod(GfxMoveMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GfxMoveMode.enumOnTarget: return new GfxOnTargetMove();
|
||||
case GfxMoveMode.enumLinearMove: return new GfxLinearMove();
|
||||
default: return new GfxLinearMove(); // fallback
|
||||
case GfxMoveMode.enumOnTarget: return new CGfxOnTargetMove(mode);
|
||||
case GfxMoveMode.enumLinearMove: return new CGfxLinearMove(mode);
|
||||
default: return new CGfxLinearMove(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`CGfxLinearMove.cs`** — Straight-line projectile
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
namespace BrewMonster
|
||||
{
|
||||
// Mirrors C++ CGfxLinearMove exactly (A3DSkillGfxEvent2.cpp:22-52)
|
||||
public class CGfxLinearMove : CGfxMoveBase
|
||||
{
|
||||
protected float m_fSpeed;
|
||||
private const float _fly_speed = 20.0f / 1000.0f; // same as C++
|
||||
|
||||
public CGfxLinearMove(GfxMoveMode mode) : base(mode) { }
|
||||
|
||||
public override void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
if (m_bArea)
|
||||
{
|
||||
CalcRange((vTarget - vHost).normalized);
|
||||
m_vPos = vHost + GetRandOff();
|
||||
}
|
||||
else
|
||||
m_vPos = vHost;
|
||||
|
||||
m_vMoveDir = vTarget - m_vPos;
|
||||
float fDist = Normalize(ref m_vMoveDir);
|
||||
float fMax = _fly_speed * m_dwMaxFlyTime;
|
||||
|
||||
if (fMax >= fDist)
|
||||
m_fSpeed = _fly_speed;
|
||||
else
|
||||
m_fSpeed = fDist / m_dwMaxFlyTime;
|
||||
}
|
||||
|
||||
public override bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
{
|
||||
Vector3 vFlyDir = vTargetPos - m_vPos;
|
||||
float fDist = Normalize(ref vFlyDir);
|
||||
float fFlyDist = m_fSpeed * dwDeltaTime;
|
||||
|
||||
if (fFlyDist >= fDist) return true; // target hit
|
||||
m_vPos += vFlyDir * fFlyDist;
|
||||
m_vMoveDir = vFlyDir;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`CGfxOnTargetMove.cs`** — Instant hit (no flight)
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
namespace BrewMonster
|
||||
{
|
||||
// Mirrors C++ CGfxOnTargetMove exactly (A3DSkillGfxEvent2.cpp:297-322)
|
||||
public class CGfxOnTargetMove : CGfxMoveBase
|
||||
{
|
||||
protected float m_fRadius;
|
||||
protected Vector3 m_vOffset;
|
||||
|
||||
public CGfxOnTargetMove(GfxMoveMode mode) : base(mode) { m_HitPos = GfxHitPos.enumHitBottom; m_fRadius = 0; }
|
||||
|
||||
public override void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
m_vPos = vTarget;
|
||||
m_vMoveDir = vTarget - vHost;
|
||||
m_vMoveDir.y = 0; // C++: zero out Y before normalize
|
||||
if (Normalize(ref m_vMoveDir) == 0)
|
||||
m_vMoveDir = Vector3.forward; // _unit_z
|
||||
|
||||
if (m_bOneOfCluser)
|
||||
{
|
||||
float fRandAng = UnityEngine.Random.value * Mathf.PI * 2f;
|
||||
float fRadius = UnityEngine.Random.value * m_fRadius;
|
||||
m_vOffset.x = Mathf.Cos(fRandAng) * fRadius;
|
||||
m_vOffset.z = Mathf.Sin(fRandAng) * fRadius;
|
||||
m_vOffset.y = 0;
|
||||
m_vPos += m_vOffset;
|
||||
}
|
||||
else
|
||||
m_vOffset = Vector3.zero;
|
||||
}
|
||||
|
||||
public override bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos)
|
||||
{
|
||||
m_vPos = vTargetPos + m_vOffset;
|
||||
return false; // C++ returns false — hit triggered by fly time timeout
|
||||
}
|
||||
|
||||
public override void SetParam(GFX_SKILL_PARAM param)
|
||||
{
|
||||
base.SetParam(param);
|
||||
m_fRadius = param.value.fVal; // C# union access: param.value.fVal
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Wire Up A3DSkillGfxEvent State Machine (1-2 days)
|
||||
|
||||
**In `A3DSkillGfxMan.cs`:**
|
||||
|
||||
1. **Add field** (un-comment line ~205):
|
||||
```csharp
|
||||
protected IGfxMovement m_pMoveMethod;
|
||||
protected CGfxMoveBase m_pMoveMethod;
|
||||
```
|
||||
|
||||
2. **In constructor** (replace line ~251):
|
||||
```csharp
|
||||
m_pMoveMethod = GfxMoveFactory.Create(mode);
|
||||
m_pMoveMethod = CGfxMoveBase.CreateMoveMethod(mode);
|
||||
```
|
||||
|
||||
3. **Un-comment in Tick():**
|
||||
@@ -195,15 +292,16 @@ namespace BrewMonster
|
||||
- Line 434-435: `m_pMoveMethod.SetMaxFlyTime()` + `StartMove()`
|
||||
- Line 454: `m_pMoveMethod.TickMove()` → `HitTarget(GetTargetCenter())`
|
||||
|
||||
4. **Un-comment setters** (lines 336-345):
|
||||
4. **Un-comment delegating setters** (lines 336-345) — these delegate to `m_pMoveMethod` which has the correct default behavior:
|
||||
```csharp
|
||||
public IGfxMovement GetMoveMethod() { return m_pMoveMethod; }
|
||||
public CGfxMoveBase GetMoveMethod() { return m_pMoveMethod; }
|
||||
public GfxMoveMode GetMode() { return m_pMoveMethod.GetMode(); }
|
||||
public GfxHitPos GetHitPos() { return m_pMoveMethod.GetHitPos(); }
|
||||
public void SetReverse(bool bReverse) { m_pMoveMethod.SetReverse(bReverse); }
|
||||
public void SetParam(GFX_SKILL_PARAM param) { m_pMoveMethod.SetParam(param); }
|
||||
public void SetIsCluster(bool bCluster) { m_pMoveMethod.SetIsCluster(bCluster); }
|
||||
```
|
||||
These work correctly because `CGfxMoveBase.SetIsCluster()` / `SetReverse()` / `SetParam()` have default implementations that store the values. Subclasses inherit the defaults unless they need to do extra work (like `CGfxMeteoricMove.SetParam()` which also stores a radius).
|
||||
|
||||
5. **In AddOneSkillGfxEvent():**
|
||||
- Un-comment line 150: `pEvent.SetReverse(bReverse)`
|
||||
@@ -352,7 +450,7 @@ if (composerMan != null)
|
||||
**Expected behavior:**
|
||||
1. Player attacks → DoFire()
|
||||
2. Composer loads: `m_MoveMode = enumOnTarget`, `m_dwFlyTime = 0`
|
||||
3. Movement: `GfxOnTargetMove.TickMove()` → returns `true` immediately
|
||||
3. Movement: `CGfxOnTargetMove.TickMove()` → returns `false` (stays at target + offset), hit triggered by fly time timeout
|
||||
4. `HitTarget()` → spawns hit GFX at target
|
||||
5. `m_timeToDoDamage = 1` → damage immediately
|
||||
|
||||
@@ -426,10 +524,9 @@ BMLogger.Log($"[GFX] Hit target at {vTarget}, hitGFX={m_pComposer.GetHitGFX()?.n
|
||||
|
||||
| File | What to Change | Phase |
|
||||
|------|----------------|-------|
|
||||
| `IGfxMovement.cs` | **CREATE** — movement interface | 1 |
|
||||
| `GfxLinearMove.cs` | **CREATE** — linear projectile | 1 |
|
||||
| `GfxOnTargetMove.cs` | **CREATE** — instant hit | 1 |
|
||||
| `GfxMoveFactory.cs` | **CREATE** — factory | 1 |
|
||||
| `CGfxMoveBase.cs` | **CREATE** — movement base class + factory (same as C++) | 1 |
|
||||
| `CGfxLinearMove.cs` | **CREATE** — linear projectile | 1 |
|
||||
| `CGfxOnTargetMove.cs` | **CREATE** — instant hit | 1 |
|
||||
| `A3DSkillGfxMan.cs` | **EDIT** — un-comment state machine, add movement field | 1 |
|
||||
| `CECSkillGfxMan.cs` | **EDIT** — un-comment Tick, add GFX spawning | 1 |
|
||||
| `CECAttacksMan.cs` | **EDIT** — fix GFX strings, remove SpawnGFX, add scale | 1 |
|
||||
@@ -438,7 +535,7 @@ BMLogger.Log($"[GFX] Hit target at {vTarget}, hitGFX={m_pComposer.GetHitGFX()?.n
|
||||
|
||||
## Estimated Time: 3-5 days for Phase 1
|
||||
|
||||
- Day 1: Create movement system (4 files)
|
||||
- Day 1: Create movement system (3 files)
|
||||
- Day 2: Un-comment + wire up state machine in A3DSkillGfxMan + CECSkillGfxMan
|
||||
- Day 3: Fix composer Play() GFX refs, remove SpawnGFX hack, add accessor methods
|
||||
- Day 4: Testing, debugging, logging
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# Naming Conventions - CRITICAL RULES
|
||||
|
||||
## ⚠️ CRITICAL: Preserve C++ Names Exactly
|
||||
|
||||
**Never change C++ class/field/method names unless explicitly requested by the user.**
|
||||
|
||||
## Class Names
|
||||
|
||||
### Movement System
|
||||
| C++ Name | C# Name | ✅/❌ |
|
||||
|----------|---------|------|
|
||||
| `CGfxMoveBase` | `CGfxMoveBase` | ✅ Correct |
|
||||
| `CGfxLinearMove` | `CGfxLinearMove` | ✅ Correct |
|
||||
| `CGfxOnTargetMove` | `CGfxOnTargetMove` | ✅ Correct |
|
||||
| `CGfxParabolicMove` | `CGfxParabolicMove` | ✅ Correct |
|
||||
| `CGfxMissileMove` | `CGfxMissileMove` | ✅ Correct |
|
||||
| `CGfxMeteoricMove` | `CGfxMeteoricMove` | ✅ Correct |
|
||||
| `CGfxHelixMove` | `CGfxHelixMove` | ✅ Correct |
|
||||
| `CGfxCurvedMove` | `CGfxCurvedMove` | ✅ Correct |
|
||||
| `CGfxAccMove` | `CGfxAccMove` | ✅ Correct |
|
||||
| `CGfxLinkMove` | `CGfxLinkMove` | ✅ Correct |
|
||||
| `CGfxRandMove` | `CGfxRandMove` | ✅ Correct |
|
||||
|
||||
**❌ WRONG:** `IGfxMovement`, `GfxLinearMove`, `LinearMovement`
|
||||
|
||||
### Event System
|
||||
| C++ Name | C# Name | ✅/❌ |
|
||||
|----------|---------|------|
|
||||
| `A3DSkillGfxEvent` | `A3DSkillGfxEvent` | ✅ Correct |
|
||||
| `A3DSkillGfxMan` | `A3DSkillGfxMan` | ✅ Correct |
|
||||
| `A3DSkillGfxComposer` | `A3DSkillGfxComposer` | ✅ Correct |
|
||||
| `A3DSkillGfxComposerMan` | `A3DSkillGfxComposerMan` | ✅ Correct |
|
||||
| `CECSkillGfxEvent` | `CECSkillGfxEvent` | ✅ Correct |
|
||||
| `CECSkillGfxMan` | `CECSkillGfxMan` | ✅ Correct |
|
||||
|
||||
## Field Names
|
||||
|
||||
### Movement Base Fields
|
||||
| C++ Name | C# Name | Notes |
|
||||
|----------|---------|-------|
|
||||
| `m_Mode` | `m_Mode` | GfxMoveMode enum |
|
||||
| `m_HitPos` | `m_HitPos` | GfxHitPos enum |
|
||||
| `m_vPos` | `m_vPos` | Position vector |
|
||||
| `m_vMoveDir` | `m_vMoveDir` | Movement direction |
|
||||
| `m_bOneOfCluser` | `m_bOneOfCluser` | ⚠️ **TYPO PRESERVED** - C++ has this spelling |
|
||||
| `m_dwMaxFlyTime` | `m_dwMaxFlyTime` | Max flight time (uint) |
|
||||
| `m_bReverse` | `m_bReverse` | Reverse direction flag |
|
||||
| `m_bArea` | `m_bArea` | Area emission flag |
|
||||
| `m_Shape` | `m_Shape` | EmitShape enum |
|
||||
| `m_vSize` | `m_vSize` | Size vector |
|
||||
| `m_vXRange` | `m_vXRange` | X range for area emission |
|
||||
| `m_vYRange` | `m_vYRange` | Y range for area emission |
|
||||
| `m_vZRange` | `m_vZRange` | Z range for area emission |
|
||||
| `m_fSquare` | `m_fSquare` | Square magnitude |
|
||||
| `m_fSquareH` | `m_fSquareH` | Horizontal square magnitude |
|
||||
|
||||
**❌ WRONG:** `m_bOneOfCluster` (correct spelling) - C++ has the typo, we must preserve it
|
||||
|
||||
### Event Fields
|
||||
| C++ Name | C# Name | Notes |
|
||||
|----------|---------|-------|
|
||||
| `m_pComposer` | `m_pComposer` | A3DSkillGfxComposer reference |
|
||||
| `m_pMoveMethod` | `m_pMoveMethod` | CGfxMoveBase reference |
|
||||
| `m_pFlyGfx` | `m_pFlyGfx` | Fly GFX instance |
|
||||
| `m_pHitGfx` | `m_pHitGfx` | Hit GFX instance |
|
||||
| `m_dwFlyTimeSpan` | `m_dwFlyTimeSpan` | Flight time span (uint) |
|
||||
| `m_dwCurSpan` | `m_dwCurSpan` | Current span (uint) |
|
||||
| `m_dwDelayTime` | `m_dwDelayTime` | Delay time (uint) |
|
||||
| `m_bTraceTarget` | `m_bTraceTarget` | Trace target flag |
|
||||
| `m_bFadeOut` | `m_bFadeOut` | Fade out flag |
|
||||
| `m_nHostID` | `m_nHostID` | Host ID (long/clientid_t) |
|
||||
| `m_nTargetID` | `m_nTargetID` | Target ID (long/clientid_t) |
|
||||
| `m_dwModifier` | `m_dwModifier` | Modifier (uint) |
|
||||
| `m_bIsGoblinSkill` | `m_bIsGoblinSkill` | Goblin skill flag |
|
||||
| `m_vHostPos` | `m_vHostPos` | Host position |
|
||||
| `m_vTargetPos` | `m_vTargetPos` | Target position |
|
||||
| `m_vTargetDir` | `m_vTargetDir` | Target direction |
|
||||
| `m_vTargetUp` | `m_vTargetUp` | Target up vector |
|
||||
| `m_bHostExist` | `m_bHostExist` | Host exists flag |
|
||||
| `m_bTargetExist` | `m_bTargetExist` | Target exists flag |
|
||||
| `m_bHitGfxInfinite` | `m_bHitGfxInfinite` | Hit GFX infinite flag |
|
||||
| `m_bTargetDirAndUpExist` | `m_bTargetDirAndUpExist` | Target dir/up exist flag |
|
||||
| `m_enumState` | `m_enumState` | GfxSkillEventState enum |
|
||||
|
||||
## Method Names
|
||||
|
||||
### Movement Methods
|
||||
| C++ Name | C# Name | Notes |
|
||||
|----------|---------|-------|
|
||||
| `StartMove()` | `StartMove()` | Initialize movement |
|
||||
| `TickMove()` | `TickMove()` | Update movement (returns bool) |
|
||||
| `SetParam()` | `SetParam()` | Set GFX_SKILL_PARAM |
|
||||
| `SetIsCluster()` | `SetIsCluster()` | Set cluster flag |
|
||||
| `SetReverse()` | `SetReverse()` | Set reverse flag |
|
||||
| `UpdateGfxParam()` | `UpdateGfxParam()` | Update GFX parameters |
|
||||
| `GetMode()` | `GetMode()` | Get movement mode |
|
||||
| `GetHitPos()` | `GetHitPos()` | Get hit position |
|
||||
| `GetPos()` | `GetPos()` | Get current position |
|
||||
| `GetMoveDir()` | `GetMoveDir()` | Get movement direction |
|
||||
| `IsReverse()` | `IsReverse()` | Check reverse flag |
|
||||
| `SetMaxFlyTime()` | `SetMaxFlyTime()` | Set max flight time |
|
||||
| `CreateMoveMethod()` | `CreateMoveMethod()` | Static factory method |
|
||||
|
||||
### Event Methods
|
||||
| C++ Name | C# Name | Notes |
|
||||
|----------|---------|-------|
|
||||
| `Tick()` | `Tick()` | Update event |
|
||||
| `HitTarget()` | `HitTarget()` | Handle target hit |
|
||||
| `GetTargetCenter()` | `GetTargetCenter()` | Get target center (abstract) |
|
||||
| `GetTargetDirAndUp()` | `GetTargetDirAndUp()` | Get target direction/up |
|
||||
| `SetComposer()` | `SetComposer()` | Set composer |
|
||||
| `GetMoveMethod()` | `GetMoveMethod()` | Get movement method |
|
||||
| `Resume()` | `Resume()` | Resume event |
|
||||
|
||||
## Enum Names
|
||||
|
||||
| C++ Name | C# Name | Notes |
|
||||
|----------|---------|-------|
|
||||
| `GfxMoveMode` | `GfxMoveMode` | Movement mode enum |
|
||||
| `GfxHitPos` | `GfxHitPos` | Hit position enum |
|
||||
| `EmitShape` | `EmitShape` | Emission shape enum |
|
||||
| `GfxSkillValType` | `GfxSkillValType` | Value type enum |
|
||||
| `GfxSkillEventState` | `GfxSkillEventState` | Event state enum |
|
||||
|
||||
## Enum Values
|
||||
|
||||
### GfxMoveMode
|
||||
- `enumLinearMove` (0)
|
||||
- `enumParabolicMove` (1)
|
||||
- `enumMissileMove` (2)
|
||||
- `enumMeteoricMove` (3)
|
||||
- `enumHelixMove` (4)
|
||||
- `enumCurvedMove` (5)
|
||||
- `enumAccMove` (6)
|
||||
- `enumOnTarget` (7)
|
||||
- `enumLink` (8)
|
||||
- `enumRandMove` (9)
|
||||
- `enumMoveModeNum` (10)
|
||||
|
||||
### GfxHitPos
|
||||
- `enumHitCenter` (0)
|
||||
- `enumHitBottom` (1)
|
||||
|
||||
### EmitShape
|
||||
- `enumBox` (0)
|
||||
- `enumSphere` (1)
|
||||
- `enumCylinder` (2)
|
||||
- `enumShapeNum` (3)
|
||||
|
||||
### GfxSkillEventState
|
||||
- `enumWait` (0)
|
||||
- `enumFlying` (1)
|
||||
- `enumHit` (2)
|
||||
- `enumFinished` (3)
|
||||
|
||||
## File Names
|
||||
|
||||
| C++ File | C# File | Location |
|
||||
|----------|---------|----------|
|
||||
| `A3DSkillGfxEvent2.h` | `CGfxMoveBase.cs` | `Assets/PerfectWorld/Scripts/Vfx/` |
|
||||
| `A3DSkillGfxEvent2.cpp` | `CGfxLinearMove.cs` | `Assets/PerfectWorld/Scripts/Vfx/` |
|
||||
| `A3DSkillGfxEvent2.cpp` | `CGfxOnTargetMove.cs` | `Assets/PerfectWorld/Scripts/Vfx/` |
|
||||
| `A3DSkillGfxMan.h` | `A3DSkillGfxMan.cs` | `Assets/PerfectWorld/Scripts/Managers/` |
|
||||
| `CECSkillGfxMan.h` | `CECSkillGfxMan.cs` | `Assets/PerfectWorld/Scripts/Managers/` |
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
### Hungarian Notation (Preserved from C++)
|
||||
- `m_` prefix = member variable
|
||||
- `p` prefix = pointer/reference (C#: reference)
|
||||
- `n` prefix = number/ID
|
||||
- `dw` prefix = DWORD (uint)
|
||||
- `b` prefix = bool
|
||||
- `f` prefix = float
|
||||
- `v` prefix = vector
|
||||
- `sz` prefix = string (C#: string)
|
||||
|
||||
### Examples
|
||||
- `m_pMoveMethod` = member pointer/reference to MoveMethod
|
||||
- `m_dwFlyTimeSpan` = member DWORD (uint) for fly time span
|
||||
- `m_bTraceTarget` = member bool for trace target
|
||||
- `m_vHostPos` = member vector for host position
|
||||
- `szFlyGfx` = string for fly GFX path
|
||||
|
||||
## Rules Summary
|
||||
|
||||
1. **Preserve ALL C++ names exactly** - including typos (`m_bOneOfCluser`)
|
||||
2. **Keep Hungarian notation** - maintain `m_`, `p`, `dw`, `b`, `f`, `v` prefixes
|
||||
3. **Match enum values** - exact same names and order
|
||||
4. **Preserve file structure** - similar organization to C++
|
||||
5. **Never "improve" names** - unless user explicitly requests it
|
||||
@@ -0,0 +1,286 @@
|
||||
# Type Mappings - C++ to C# Conversions
|
||||
|
||||
## Primitive Types
|
||||
|
||||
| C++ Type | C# Type | Notes |
|
||||
|----------|---------|-------|
|
||||
| `DWORD` | `uint` | 32-bit unsigned integer |
|
||||
| `bool` | `bool` | Boolean |
|
||||
| `int` | `int` | 32-bit signed integer |
|
||||
| `float` | `float` | 32-bit floating point |
|
||||
| `double` | `double` | 64-bit floating point |
|
||||
| `char*` | `string` | String (C# uses managed strings) |
|
||||
| `const char*` | `string` | Constant string |
|
||||
| `size_t` | `uint` or `ulong` | Size type (usually uint) |
|
||||
| `clientid_t` | `long` | Client ID type |
|
||||
|
||||
## Vector Types
|
||||
|
||||
| C++ Type | C# Type | Conversion Notes |
|
||||
|----------|---------|------------------|
|
||||
| `A3DVECTOR3` | `Vector3` | Unity's Vector3 |
|
||||
| `A3DVECTOR3&` | `Vector3` (ref) | Pass by reference |
|
||||
| `const A3DVECTOR3&` | `Vector3` (ref) | Const reference |
|
||||
|
||||
### A3DVECTOR3 to Vector3 Conversion
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
A3DVECTOR3 vPos;
|
||||
vPos.x = 1.0f;
|
||||
vPos.y = 2.0f;
|
||||
vPos.z = 3.0f;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
Vector3 vPos = new Vector3(1.0f, 2.0f, 3.0f);
|
||||
// Or
|
||||
Vector3 vPos;
|
||||
vPos.x = 1.0f;
|
||||
vPos.y = 2.0f;
|
||||
vPos.z = 3.0f;
|
||||
```
|
||||
|
||||
**When accessing from A3DVECTOR3 struct:**
|
||||
```csharp
|
||||
// If param.m_vSize is A3DVECTOR3
|
||||
m_vSize = new Vector3(param.m_vSize.x, param.m_vSize.y, param.m_vSize.z);
|
||||
```
|
||||
|
||||
## Pointer Types
|
||||
|
||||
| C++ Type | C# Type | Notes |
|
||||
|----------|---------|-------|
|
||||
| `T*` | `T` (reference) | C# uses references, not pointers |
|
||||
| `T*&` | `ref T` | Reference to pointer → ref reference |
|
||||
| `const T*` | `T` (readonly) | Const pointer → readonly reference |
|
||||
| `T**` | `ref T` or `T[]` | Pointer to pointer → ref or array |
|
||||
|
||||
### Examples
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
A3DSkillGfxComposer* m_pComposer;
|
||||
CGfxMoveBase* m_pMoveMethod;
|
||||
void SetComposer(A3DSkillGfxComposer* pComposer);
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
A3DSkillGfxComposer m_pComposer;
|
||||
CGfxMoveBase m_pMoveMethod;
|
||||
void SetComposer(A3DSkillGfxComposer pComposer);
|
||||
```
|
||||
|
||||
## Struct Types
|
||||
|
||||
### GFX_SKILL_PARAM
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
struct GFX_SKILL_PARAM
|
||||
{
|
||||
union
|
||||
{
|
||||
bool bVal;
|
||||
int nVal;
|
||||
float fVal;
|
||||
};
|
||||
bool m_bArea;
|
||||
EmitShape m_Shape;
|
||||
A3DVECTOR3 m_vSize;
|
||||
};
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct GFX_SKILL_PARAM
|
||||
{
|
||||
public ValueUnion value; // Union wrapped in struct
|
||||
|
||||
public bool m_bArea;
|
||||
public EmitShape m_Shape;
|
||||
public A3DVECTOR3 m_vSize; // Note: Uses A3DVECTOR3, not Vector3
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ValueUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public bool bVal;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public int nVal;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public float fVal;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Access Pattern:**
|
||||
```csharp
|
||||
// C++: param.fVal
|
||||
// C#: param.value.fVal
|
||||
m_fRadius = param.value.fVal;
|
||||
```
|
||||
|
||||
## Enum Types
|
||||
|
||||
| C++ Enum | C# Enum | Notes |
|
||||
|----------|---------|-------|
|
||||
| `enum GfxMoveMode { ... }` | `public enum GfxMoveMode { ... }` | Same values |
|
||||
| `enum GfxHitPos { ... }` | `public enum GfxHitPos { ... }` | Same values |
|
||||
| `enum EmitShape { ... }` | `public enum EmitShape { ... }` | Same values |
|
||||
|
||||
**Preserve exact enum value names and order!**
|
||||
|
||||
## Function Signatures
|
||||
|
||||
### Method Parameters
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
bool TickMove(DWORD dwDeltaTime, const A3DVECTOR3& vHostPos, const A3DVECTOR3& vTargetPos);
|
||||
void SetParam(const GFX_SKILL_PARAM* param);
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos);
|
||||
void SetParam(GFX_SKILL_PARAM param); // Struct passed by value (or ref if needed)
|
||||
```
|
||||
|
||||
### Return Types
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
CGfxMoveBase* CreateMoveMethod(GfxMoveMode mode);
|
||||
const A3DVECTOR3& GetPos() const;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
static CGfxMoveBase CreateMoveMethod(GfxMoveMode mode);
|
||||
Vector3 GetPos(); // No const in C#
|
||||
```
|
||||
|
||||
## Const Correctness
|
||||
|
||||
| C++ | C# Equivalent |
|
||||
|-----|---------------|
|
||||
| `const T&` | `T` (value type) or `readonly T` (reference) |
|
||||
| `const T*` | `T` (reference, readonly) |
|
||||
| `const` method | No equivalent (use `readonly` for fields) |
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
const A3DVECTOR3& GetPos() const;
|
||||
bool IsReverse() const;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
Vector3 GetPos(); // No const modifier needed
|
||||
bool IsReverse(); // No const modifier needed
|
||||
```
|
||||
|
||||
## Memory Management
|
||||
|
||||
| C++ | C# |
|
||||
|-----|-----|
|
||||
| `new T()` | `new T()` |
|
||||
| `delete ptr` | Automatic GC (or `Dispose()` for IDisposable) |
|
||||
| `delete[] arr` | Automatic GC |
|
||||
| `malloc/free` | Automatic GC |
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
CGfxMoveBase* pMove = new CGfxLinearMove(mode);
|
||||
delete pMove;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
CGfxMoveBase pMove = new CGfxLinearMove(mode);
|
||||
// Automatic garbage collection, or implement IDisposable if needed
|
||||
```
|
||||
|
||||
## Static Constants
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
static const float _fly_speed = 20.0f / 1000.0f;
|
||||
static const float _gravity = 9.8e-6f;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
private const float _fly_speed = 20.0f / 1000.0f;
|
||||
private const float _gravity = 9.8e-6f;
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### Normalize
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
float Normalize(A3DVECTOR3& v)
|
||||
{
|
||||
float mag = v.Magnitude();
|
||||
if (mag < 1e-6f) { v = _unit_zero; return 0f; }
|
||||
v /= mag;
|
||||
return mag;
|
||||
}
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
protected static float Normalize(ref Vector3 v)
|
||||
{
|
||||
float mag = v.magnitude;
|
||||
if (mag < 1e-6f) { v = Vector3.zero; return 0f; }
|
||||
v /= mag;
|
||||
return mag;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Passing by Reference
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
void CalcRange(const A3DVECTOR3& vDir);
|
||||
bool GetTargetDirAndUp(A3DVECTOR3& vDir, A3DVECTOR3& vUp);
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
void CalcRange(Vector3 vDir); // Value type, passed by value
|
||||
bool GetTargetDirAndUp(out Vector3 vDir, out Vector3 vUp); // Use out/ref
|
||||
```
|
||||
|
||||
### Array/Container Types
|
||||
|
||||
| C++ | C# |
|
||||
|-----|-----|
|
||||
| `std::vector<T>` | `List<T>` |
|
||||
| `std::list<T>` | `List<T>` or `LinkedList<T>` |
|
||||
| `std::map<K,V>` | `Dictionary<K,V>` |
|
||||
| `T[]` | `T[]` |
|
||||
|
||||
## Type Conversion Checklist
|
||||
|
||||
When converting a method:
|
||||
- [ ] `DWORD` → `uint`
|
||||
- [ ] `A3DVECTOR3` → `Vector3`
|
||||
- [ ] `T*` → `T` (remove pointer)
|
||||
- [ ] `const T&` → `T` (remove const and reference)
|
||||
- [ ] `const T*` → `T` (remove const and pointer)
|
||||
- [ ] `char*` → `string`
|
||||
- [ ] `clientid_t` → `long`
|
||||
- [ ] Union access: `param.fVal` → `param.value.fVal`
|
||||
- [ ] `A3DVECTOR3` struct fields → `Vector3` constructor
|
||||
@@ -0,0 +1,418 @@
|
||||
# Code Patterns - C++ to C# Conversions
|
||||
|
||||
## Abstract Base Classes
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
class CGfxMoveBase
|
||||
{
|
||||
protected:
|
||||
CGfxMoveBase(GfxMoveMode mode) : m_Mode(mode) {}
|
||||
|
||||
public:
|
||||
virtual ~CGfxMoveBase() {}
|
||||
virtual void StartMove(const A3DVECTOR3& vHost, const A3DVECTOR3& vTarget) = 0;
|
||||
virtual bool TickMove(DWORD dwDeltaTime, ...) = 0;
|
||||
virtual void SetParam(const GFX_SKILL_PARAM* param) { /* default impl */ }
|
||||
static CGfxMoveBase* CreateMoveMethod(GfxMoveMode mode);
|
||||
};
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
public abstract class CGfxMoveBase
|
||||
{
|
||||
protected CGfxMoveBase(GfxMoveMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
}
|
||||
|
||||
// Pure virtual (= 0) → abstract
|
||||
public abstract void StartMove(Vector3 vHost, Vector3 vTarget);
|
||||
public abstract bool TickMove(uint dwDeltaTime, ...);
|
||||
|
||||
// Virtual with default → virtual
|
||||
public virtual void SetParam(GFX_SKILL_PARAM param)
|
||||
{
|
||||
// default implementation
|
||||
}
|
||||
|
||||
// Static factory method
|
||||
public static CGfxMoveBase CreateMoveMethod(GfxMoveMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GfxMoveMode.enumLinearMove: return new CGfxLinearMove(mode);
|
||||
default: return new CGfxLinearMove(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Inheritance Patterns
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
class CGfxLinearMove : public CGfxMoveBase
|
||||
{
|
||||
protected:
|
||||
float m_fSpeed;
|
||||
|
||||
public:
|
||||
CGfxLinearMove(GfxMoveMode mode) : CGfxMoveBase(mode) {}
|
||||
virtual void StartMove(const A3DVECTOR3& vHost, const A3DVECTOR3& vTarget);
|
||||
virtual bool TickMove(DWORD dwDeltaTime, ...);
|
||||
};
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
public class CGfxLinearMove : CGfxMoveBase
|
||||
{
|
||||
protected float m_fSpeed;
|
||||
|
||||
public CGfxLinearMove(GfxMoveMode mode) : base(mode) { }
|
||||
|
||||
public override void StartMove(Vector3 vHost, Vector3 vTarget)
|
||||
{
|
||||
// implementation
|
||||
}
|
||||
|
||||
public override bool TickMove(uint dwDeltaTime, ...)
|
||||
{
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Static Factory Methods
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
inline CGfxMoveBase* CGfxMoveBase::CreateMoveMethod(GfxMoveMode mode)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case enumLinearMove:
|
||||
return new CGfxLinearMove(mode);
|
||||
case enumOnTarget:
|
||||
return new CGfxOnTargetMove(mode);
|
||||
default:
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
public static CGfxMoveBase CreateMoveMethod(GfxMoveMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GfxMoveMode.enumLinearMove:
|
||||
return new CGfxLinearMove(mode);
|
||||
case GfxMoveMode.enumOnTarget:
|
||||
return new CGfxOnTargetMove(mode);
|
||||
default:
|
||||
return new CGfxLinearMove(mode); // fallback
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Vector Operations
|
||||
|
||||
### Normalize Pattern
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
A3DVECTOR3 vDir = vTarget - m_vPos;
|
||||
float fDist = vDir.Normalize(); // In-place normalize, returns magnitude
|
||||
if (fDist < 1e-4f) return true;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
Vector3 vDir = vTarget - m_vPos;
|
||||
float fDist = Normalize(ref vDir); // Helper method
|
||||
if (fDist < 1e-4f) return true;
|
||||
|
||||
// Helper implementation:
|
||||
protected static float Normalize(ref Vector3 v)
|
||||
{
|
||||
float mag = v.magnitude;
|
||||
if (mag < 1e-6f) { v = Vector3.zero; return 0f; }
|
||||
v /= mag;
|
||||
return mag;
|
||||
}
|
||||
```
|
||||
|
||||
### Vector Math
|
||||
|
||||
**C++:**
|
||||
```cpp
|
||||
m_vPos += vFlyDir * fFlyDist;
|
||||
m_vMoveDir = vFlyDir;
|
||||
```
|
||||
|
||||
**C#:**
|
||||
```csharp
|
||||
m_vPos += vFlyDir * fFlyDist;
|
||||
m_vMoveDir = vFlyDir;
|
||||
```
|
||||
|
||||
## Protected Helper Methods
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
protected:
|
||||
void CalcRange(const A3DVECTOR3& vDir)
|
||||
{
|
||||
m_vYRange = _unit_y;
|
||||
m_vZRange.Set(vDir.x, 0, vDir.z);
|
||||
if (m_vZRange.Normalize() < .01f) m_vZRange = _unit_z;
|
||||
m_vXRange = CrossProduct(m_vYRange, m_vZRange);
|
||||
// ...
|
||||
}
|
||||
|
||||
A3DVECTOR3 GetRandOff() const
|
||||
{
|
||||
if (m_Shape == enumBox)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
protected void CalcRange(Vector3 vDir)
|
||||
{
|
||||
m_vYRange = Vector3.up;
|
||||
m_vZRange = new Vector3(vDir.x, 0, vDir.z);
|
||||
if (Normalize(ref m_vZRange) < 0.01f) m_vZRange = Vector3.forward;
|
||||
m_vXRange = Vector3.Cross(m_vYRange, m_vZRange);
|
||||
// ...
|
||||
}
|
||||
|
||||
protected Vector3 GetRandOff()
|
||||
{
|
||||
if (m_Shape == EmitShape.enumBox)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## State Machine Pattern
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
enum
|
||||
{
|
||||
enumWait,
|
||||
enumFlying,
|
||||
enumHit,
|
||||
enumFinished
|
||||
} m_enumState;
|
||||
|
||||
void Tick(DWORD dwDeltaTime)
|
||||
{
|
||||
if (m_enumState == enumFinished) return;
|
||||
else if (m_enumState == enumHit)
|
||||
{
|
||||
// handle hit state
|
||||
}
|
||||
else if (m_enumState == enumWait)
|
||||
{
|
||||
if (m_dwCurSpan < m_dwDelayTime) return;
|
||||
m_enumState = enumFlying;
|
||||
}
|
||||
else // enumFlying
|
||||
{
|
||||
if (m_pMoveMethod.TickMove(dwDeltaTime, ...))
|
||||
HitTarget(GetTargetCenter());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
protected GfxSkillEventState m_enumState;
|
||||
|
||||
public virtual void Tick(uint dwDeltaTime)
|
||||
{
|
||||
if (m_enumState == GfxSkillEventState.enumFinished) return;
|
||||
else if (m_enumState == GfxSkillEventState.enumHit)
|
||||
{
|
||||
// handle hit state
|
||||
}
|
||||
else if (m_enumState == GfxSkillEventState.enumWait)
|
||||
{
|
||||
if (m_dwCurSpan < m_dwDelayTime) return;
|
||||
m_enumState = GfxSkillEventState.enumFlying;
|
||||
}
|
||||
else // enumFlying
|
||||
{
|
||||
if (m_pMoveMethod.TickMove(dwDeltaTime, ...))
|
||||
HitTarget(GetTargetCenter());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Const Correctness → Readonly
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
const A3DVECTOR3& GetPos() const { return m_vPos; }
|
||||
GfxMoveMode GetMode() const { return m_Mode; }
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
public Vector3 GetPos() { return m_vPos; } // No const needed
|
||||
public GfxMoveMode GetMode() { return m_Mode; } // No const needed
|
||||
```
|
||||
|
||||
## Delegate Pattern (Virtual Methods)
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
class A3DSkillGfxEvent
|
||||
{
|
||||
CGfxMoveBase* m_pMoveMethod;
|
||||
|
||||
public:
|
||||
GfxMoveMode GetMode() const { return m_pMoveMethod->GetMode(); }
|
||||
void SetReverse(bool bReverse) const { m_pMoveMethod->SetReverse(bReverse); }
|
||||
void SetParam(const GFX_SKILL_PARAM* param) { m_pMoveMethod->SetParam(param); }
|
||||
};
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
public class A3DSkillGfxEvent
|
||||
{
|
||||
protected CGfxMoveBase m_pMoveMethod;
|
||||
|
||||
public GfxMoveMode GetMode() { return m_pMoveMethod.GetMode(); }
|
||||
public void SetReverse(bool bReverse) { m_pMoveMethod.SetReverse(bReverse); }
|
||||
public void SetParam(GFX_SKILL_PARAM param) { m_pMoveMethod.SetParam(param); }
|
||||
}
|
||||
```
|
||||
|
||||
## Random Number Generation
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
float _SymmetricRandom() { return (rand() / (float)RAND_MAX) * 2.0f - 1.0f; }
|
||||
float _UnitRandom() { return rand() / (float)RAND_MAX; }
|
||||
|
||||
A3DVECTOR3 GetRandOff() const
|
||||
{
|
||||
if (m_Shape == enumBox)
|
||||
{
|
||||
A3DVECTOR3 xOff, yOff, zOff;
|
||||
xOff = _SymmetricRandom() * m_vXRange;
|
||||
yOff = _SymmetricRandom() * m_vYRange;
|
||||
zOff = _SymmetricRandom() * m_vZRange;
|
||||
return xOff + yOff + zOff;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
private static float SymRandom()
|
||||
{
|
||||
return UnityEngine.Random.value * 2f - 1f;
|
||||
}
|
||||
|
||||
private static float UnitRandom()
|
||||
{
|
||||
return UnityEngine.Random.value;
|
||||
}
|
||||
|
||||
protected Vector3 GetRandOff()
|
||||
{
|
||||
if (m_Shape == EmitShape.enumBox)
|
||||
{
|
||||
Vector3 xOff = SymRandom() * m_vXRange;
|
||||
Vector3 yOff = SymRandom() * m_vYRange;
|
||||
Vector3 zOff = SymRandom() * m_vZRange;
|
||||
return xOff + yOff + zOff;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conditional Compilation → #if UNITY_EDITOR
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
#ifdef _DEBUG
|
||||
assert(false);
|
||||
#endif
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Assert(false);
|
||||
#endif
|
||||
```
|
||||
|
||||
## Memory Management Patterns
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
void ReleaseFlyGfx()
|
||||
{
|
||||
if (m_pFlyGfx)
|
||||
{
|
||||
if (m_bFadeOut)
|
||||
AfxGetGFXExMan()->QueueFadeOutGfx(m_pFlyGfx, 1000);
|
||||
else
|
||||
{
|
||||
m_pFlyGfx->Release();
|
||||
delete m_pFlyGfx;
|
||||
}
|
||||
m_pFlyGfx = NULL;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### C# Pattern
|
||||
```csharp
|
||||
protected void ReleaseFlyGfx()
|
||||
{
|
||||
if (m_pFlyGfx != null)
|
||||
{
|
||||
if (m_bFadeOut)
|
||||
AfxGetGFXExMan().QueueFadeOutGfx(m_pFlyGfx, 1000);
|
||||
else
|
||||
{
|
||||
m_pFlyGfx.Release();
|
||||
// C#: Automatic garbage collection, no delete needed
|
||||
}
|
||||
m_pFlyGfx = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Conversion Checklist
|
||||
|
||||
When converting a class:
|
||||
- [ ] Abstract base → `abstract class`
|
||||
- [ ] Pure virtual methods → `abstract` methods
|
||||
- [ ] Virtual with default → `virtual` methods
|
||||
- [ ] Non-virtual → regular methods
|
||||
- [ ] Static factory → `static` method
|
||||
- [ ] Const methods → regular methods (no const)
|
||||
- [ ] Pointers → references (remove `*`)
|
||||
- [ ] References → value types or `ref`/`out`
|
||||
- [ ] `DWORD` → `uint`
|
||||
- [ ] `A3DVECTOR3` → `Vector3`
|
||||
- [ ] Union access → `param.value.fVal`
|
||||
- [ ] Hungarian notation → preserved
|
||||
@@ -0,0 +1,250 @@
|
||||
# Architecture Understanding
|
||||
|
||||
## System Overview
|
||||
|
||||
The Skill GFX system manages visual effects when skills are cast in Perfect World. It handles projectile flight, hit effects, and various movement patterns.
|
||||
|
||||
## High-Level Flow
|
||||
|
||||
```
|
||||
Player Casts Skill
|
||||
↓
|
||||
CECAttacksMan.AddSkillAttack()
|
||||
↓
|
||||
CECAttackEvent.Tick() - Timing system
|
||||
↓
|
||||
CECAttackEvent.DoFire() - Triggers when skill fires
|
||||
↓
|
||||
A3DSkillGfxComposerMan.Play() - Finds composer by skill ID
|
||||
↓
|
||||
A3DSkillGfxComposer.Play() - Iterates targets, calls AddOneTarget
|
||||
↓
|
||||
A3DSkillGfxMan.AddSkillGfxEvent() - Creates GFX event
|
||||
↓
|
||||
A3DSkillGfxEvent.Tick() - State machine (Wait → Flying → Hit → Finished)
|
||||
↓
|
||||
CGfxMoveBase.TickMove() - Updates projectile position
|
||||
↓
|
||||
Unity VFX System - Renders effects
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. Attack Event System
|
||||
- **CECAttacksMan**: Manages all attack events
|
||||
- **CECAttackEvent**: Individual attack event, handles timing
|
||||
- **Purpose**: Determines WHEN to trigger GFX (based on skill timing)
|
||||
|
||||
### 2. GFX Composer System
|
||||
- **A3DSkillGfxComposerMan**: Manages composers, loads from SkillStub
|
||||
- **A3DSkillGfxComposer**: Contains GFX paths and parameters for a skill
|
||||
- **Purpose**: Maps skill ID → GFX configuration
|
||||
|
||||
### 3. GFX Event System
|
||||
- **A3DSkillGfxMan**: Base manager (abstract)
|
||||
- **A3DSkillGfxEvent**: Base event class (abstract)
|
||||
- **CECSkillGfxMan**: Unity-specific manager (inherits A3DSkillGfxMan)
|
||||
- **CECSkillGfxEvent**: Unity-specific event (inherits A3DSkillGfxEvent)
|
||||
- **Purpose**: Manages active GFX instances and state machine
|
||||
|
||||
### 4. Movement System
|
||||
- **CGfxMoveBase**: Abstract base class for movement patterns
|
||||
- **CGfxLinearMove**: Straight-line projectile
|
||||
- **CGfxOnTargetMove**: Instant hit (no flight)
|
||||
- **CGfxParabolicMove**: Arc trajectory
|
||||
- **CGfxMissileMove**: Homing missile
|
||||
- **Purpose**: Calculates projectile position over time
|
||||
|
||||
## Class Hierarchy
|
||||
|
||||
### Movement Classes
|
||||
```
|
||||
CGfxMoveBase (abstract)
|
||||
├── CGfxLinearMove
|
||||
├── CGfxOnTargetMove
|
||||
├── CGfxParabolicMove
|
||||
├── CGfxMissileMove
|
||||
├── CGfxMeteoricMove
|
||||
├── CGfxHelixMove
|
||||
├── CGfxCurvedMove
|
||||
├── CGfxAccMove
|
||||
├── CGfxLinkMove
|
||||
└── CGfxRandMove
|
||||
```
|
||||
|
||||
### Event Classes
|
||||
```
|
||||
A3DSkillGfxMan (abstract)
|
||||
└── CECSkillGfxMan (Unity implementation)
|
||||
|
||||
A3DSkillGfxEvent (abstract)
|
||||
└── CECSkillGfxEvent (Unity implementation)
|
||||
```
|
||||
|
||||
### Composer Classes
|
||||
```
|
||||
A3DSkillGfxComposerMan
|
||||
└── A3DSkillGfxComposer
|
||||
```
|
||||
|
||||
## State Machine
|
||||
|
||||
### A3DSkillGfxEvent States
|
||||
|
||||
1. **enumWait**: Waiting for delay time
|
||||
- Increments `m_dwCurSpan`
|
||||
- Transitions to `enumFlying` when delay expires
|
||||
|
||||
2. **enumFlying**: Projectile in flight
|
||||
- Calls `m_pMoveMethod.TickMove()` each frame
|
||||
- Updates GFX position
|
||||
- Transitions to `enumHit` when `TickMove()` returns `true` OR timeout
|
||||
|
||||
3. **enumHit**: Hit effect playing
|
||||
- Plays hit GFX at target position
|
||||
- Transitions to `enumFinished` when hit GFX completes
|
||||
|
||||
4. **enumFinished**: Event complete
|
||||
- Event can be recycled/pooled
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Skill Configuration
|
||||
```
|
||||
SkillStub (C# data structure)
|
||||
↓
|
||||
A3DSkillGfxComposer.Load(SkillStub)
|
||||
↓
|
||||
Populates:
|
||||
- Fly GFX path
|
||||
- Hit GFX path
|
||||
- Movement mode
|
||||
- Fly time
|
||||
- Scales
|
||||
- Parameters
|
||||
```
|
||||
|
||||
### Event Creation
|
||||
```
|
||||
A3DSkillGfxComposer.Play()
|
||||
↓
|
||||
For each target:
|
||||
A3DSkillGfxMan.AddSkillGfxEvent(
|
||||
composer,
|
||||
hostID,
|
||||
targetID,
|
||||
flyGfxPath,
|
||||
hitGfxPath,
|
||||
flyTimeSpan,
|
||||
moveMode,
|
||||
...
|
||||
)
|
||||
↓
|
||||
Creates A3DSkillGfxEvent:
|
||||
- Sets composer
|
||||
- Creates movement method (CGfxMoveBase.CreateMoveMethod)
|
||||
- Sets parameters
|
||||
- Initializes state to enumWait
|
||||
```
|
||||
|
||||
### Runtime Update
|
||||
```
|
||||
A3DSkillGfxMan.Tick() (called each frame)
|
||||
↓
|
||||
For each active event:
|
||||
A3DSkillGfxEvent.Tick(deltaTime)
|
||||
↓
|
||||
State machine:
|
||||
- enumWait: Check delay
|
||||
- enumFlying:
|
||||
- m_pMoveMethod.TickMove()
|
||||
- Update GFX position
|
||||
- Check timeout
|
||||
- enumHit: Check hit GFX completion
|
||||
- enumFinished: Mark for cleanup
|
||||
```
|
||||
|
||||
## Key Relationships
|
||||
|
||||
### A3DSkillGfxEvent → CGfxMoveBase
|
||||
- **Relationship**: Composition
|
||||
- **Field**: `m_pMoveMethod : CGfxMoveBase`
|
||||
- **Usage**: Delegates movement calculation to movement method
|
||||
- **Creation**: `CGfxMoveBase.CreateMoveMethod(mode)`
|
||||
|
||||
### A3DSkillGfxEvent → A3DSkillGfxComposer
|
||||
- **Relationship**: Reference
|
||||
- **Field**: `m_pComposer : A3DSkillGfxComposer`
|
||||
- **Usage**: Accesses GFX paths and parameters
|
||||
- **Set**: `SetComposer(composer)`
|
||||
|
||||
### A3DSkillGfxMan → A3DSkillGfxEvent
|
||||
- **Relationship**: Container/Manager
|
||||
- **Storage**: List/array of active events
|
||||
- **Management**: Creates, updates, destroys events
|
||||
- **Pooling**: Reuses finished events
|
||||
|
||||
### CECSkillGfxEvent → Unity VFX
|
||||
- **Relationship**: Unity integration
|
||||
- **Fields**: `m_flyGfxInstance : GameObject`, `m_hitGfxInstance : GameObject`
|
||||
- **Usage**: Instantiates Unity particle systems/prefabs
|
||||
- **Loading**: Uses Addressables for async loading
|
||||
|
||||
## Memory Management
|
||||
|
||||
### C++ Pattern
|
||||
- Manual `new`/`delete`
|
||||
- Object pooling for events
|
||||
- GFX caching system
|
||||
|
||||
### C# Pattern
|
||||
- Automatic garbage collection
|
||||
- Object pooling still recommended for performance
|
||||
- Unity Addressables for asset loading
|
||||
|
||||
## Threading Model
|
||||
|
||||
- **C++**: Single-threaded game loop
|
||||
- **C#**: Unity main thread (MonoBehaviour.Update)
|
||||
- **Async**: Addressables loading can be async, but GFX updates on main thread
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Object Pooling**: Reuse `A3DSkillGfxEvent` instances
|
||||
2. **GFX Caching**: Cache loaded GFX prefabs
|
||||
3. **Batch Updates**: Update all events in single loop
|
||||
4. **Early Exit**: Skip finished events quickly
|
||||
5. **LOD System**: Use `m_bGfxUseLod` for distance-based quality
|
||||
|
||||
## Integration Points
|
||||
|
||||
### With Attack System
|
||||
- **Trigger**: `CECAttackEvent.DoFire()` calls `A3DSkillGfxComposerMan.Play()`
|
||||
- **Timing**: GFX starts when skill "fires" (not when cast begins)
|
||||
|
||||
### With Character System
|
||||
- **Position**: `GetTargetCenter()` gets character position
|
||||
- **Tracking**: `m_bTraceTarget` enables position updates during flight
|
||||
- **Hooks**: Future feature for bone attachment
|
||||
|
||||
### With Unity VFX
|
||||
- **Loading**: Addressables async loading
|
||||
- **Instantiation**: Unity GameObject/Prefab system
|
||||
- **Rendering**: Unity Particle System or custom VFX
|
||||
|
||||
## Extension Points
|
||||
|
||||
### New Movement Modes
|
||||
1. Create new class inheriting `CGfxMoveBase`
|
||||
2. Implement `StartMove()` and `TickMove()`
|
||||
3. Add case to `CreateMoveMethod()`
|
||||
|
||||
### New Event Types
|
||||
1. Create new class inheriting `A3DSkillGfxEvent` or `CECSkillGfxEvent`
|
||||
2. Override `GetTargetCenter()` and other virtual methods
|
||||
3. Implement Unity-specific rendering
|
||||
|
||||
### Custom GFX Systems
|
||||
1. Override `LoadFlyGfx()` / `LoadHitGfx()`
|
||||
2. Implement custom VFX loading/instantiation
|
||||
3. Use Unity Addressables or custom asset system
|
||||
@@ -0,0 +1,333 @@
|
||||
# Skill GFX System Deep Dive
|
||||
|
||||
## Movement System Architecture
|
||||
|
||||
### CGfxMoveBase Design
|
||||
|
||||
**Purpose**: Abstract base for all projectile movement patterns.
|
||||
|
||||
**Key Responsibilities**:
|
||||
- Calculate position over time
|
||||
- Handle area emission (random offsets)
|
||||
- Support clustering (multiple projectiles)
|
||||
- Provide position/direction queries
|
||||
|
||||
**Core Methods**:
|
||||
```csharp
|
||||
public abstract void StartMove(Vector3 vHost, Vector3 vTarget);
|
||||
public abstract bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos);
|
||||
```
|
||||
|
||||
**Return Value Semantics**:
|
||||
- `StartMove()`: Initialize movement, set initial position/direction
|
||||
- `TickMove()`:
|
||||
- Returns `true` = target reached/hit
|
||||
- Returns `false` = still in flight
|
||||
- Updates `m_vPos` and `m_vMoveDir`
|
||||
|
||||
### Movement Mode Details
|
||||
|
||||
#### CGfxLinearMove
|
||||
- **Pattern**: Straight line from host to target
|
||||
- **Speed**: `_fly_speed = 20.0f / 1000.0f` (units per millisecond)
|
||||
- **Behavior**:
|
||||
- If max fly time allows, uses constant speed
|
||||
- Otherwise, calculates speed to reach target in max time
|
||||
- Updates direction each frame to track moving target
|
||||
|
||||
#### CGfxOnTargetMove
|
||||
- **Pattern**: Instant hit (no flight)
|
||||
- **Behavior**:
|
||||
- `StartMove()`: Sets position to target immediately
|
||||
- `TickMove()`: Returns `false` (stays at target)
|
||||
- Hit triggered by fly time timeout, NOT by `TickMove()` return
|
||||
- Supports cluster offset (random radius around target)
|
||||
|
||||
#### CGfxParabolicMove
|
||||
- **Pattern**: Arc trajectory with gravity
|
||||
- **Physics**:
|
||||
- Horizontal velocity constant
|
||||
- Vertical velocity affected by gravity
|
||||
- Calculates initial vertical velocity to reach target
|
||||
|
||||
#### CGfxMissileMove
|
||||
- **Pattern**: Homing missile
|
||||
- **Behavior**:
|
||||
- Accelerates toward target
|
||||
- Rotates to face target
|
||||
- Constant acceleration until hit
|
||||
|
||||
#### CGfxMeteoricMove
|
||||
- **Pattern**: Falls from sky
|
||||
- **Behavior**:
|
||||
- Starts above target
|
||||
- Falls straight down
|
||||
- Configurable radius
|
||||
|
||||
#### CGfxHelixMove
|
||||
- **Pattern**: Spiral path
|
||||
- **Behavior**:
|
||||
- Spiral around center axis
|
||||
- Radius can shrink over time
|
||||
- Configurable radius parameter
|
||||
|
||||
#### CGfxCurvedMove
|
||||
- **Pattern**: Bezier-like curve
|
||||
- **Behavior**:
|
||||
- Curves from host to target
|
||||
- Can curve left or right
|
||||
- Configurable lateral speed
|
||||
|
||||
#### CGfxAccMove
|
||||
- **Pattern**: Accelerated flight
|
||||
- **Behavior**:
|
||||
- Constant acceleration
|
||||
- Configurable acceleration value
|
||||
|
||||
#### CGfxLinkMove
|
||||
- **Pattern**: Lightning chain
|
||||
- **Behavior**:
|
||||
- Links between host and target
|
||||
- Updates GFX parameters dynamically
|
||||
- No movement (stays linked)
|
||||
|
||||
#### CGfxRandMove
|
||||
- **Pattern**: Random walk
|
||||
- **Behavior**:
|
||||
- Random direction changes
|
||||
- Configurable step size and speed
|
||||
- Uses control points for smoothness
|
||||
|
||||
## Area Emission System
|
||||
|
||||
### Purpose
|
||||
Emit multiple projectiles in a random area around the start position.
|
||||
|
||||
### Configuration
|
||||
- **m_bArea**: Enable area emission
|
||||
- **m_Shape**: Emission shape (Box, Sphere, Cylinder)
|
||||
- **m_vSize**: Size of emission area
|
||||
|
||||
### Implementation
|
||||
|
||||
**CalcRange()**: Calculates X/Y/Z range vectors based on movement direction.
|
||||
```csharp
|
||||
protected void CalcRange(Vector3 vDir)
|
||||
{
|
||||
m_vYRange = Vector3.up;
|
||||
m_vZRange = new Vector3(vDir.x, 0, vDir.z); // Project to horizontal
|
||||
if (Normalize(ref m_vZRange) < 0.01f) m_vZRange = Vector3.forward;
|
||||
m_vXRange = Vector3.Cross(m_vYRange, m_vZRange); // Perpendicular
|
||||
// Scale by m_vSize
|
||||
}
|
||||
```
|
||||
|
||||
**GetRandOff()**: Generates random offset based on shape.
|
||||
- **Box**: Uniform distribution in box
|
||||
- **Sphere**: Uniform distribution in sphere (rejection sampling)
|
||||
- **Cylinder**: Uniform in horizontal circle, uniform in Y
|
||||
|
||||
### Usage
|
||||
```csharp
|
||||
if (m_bArea)
|
||||
{
|
||||
CalcRange((vTarget - vHost).normalized);
|
||||
m_vPos = vHost + GetRandOff();
|
||||
}
|
||||
```
|
||||
|
||||
## Clustering System
|
||||
|
||||
### Purpose
|
||||
Emit multiple projectiles with time intervals (e.g., machine gun effect).
|
||||
|
||||
### Configuration
|
||||
- **m_bOneOfCluser**: This projectile is part of a cluster
|
||||
- **Cluster params**: Count and interval (handled by manager)
|
||||
|
||||
### OnTarget Clustering
|
||||
For `CGfxOnTargetMove`, clustering adds random offset:
|
||||
```csharp
|
||||
if (m_bOneOfCluser)
|
||||
{
|
||||
float fRandAng = Random.value * 2π;
|
||||
float fRadius = Random.value * m_fRadius;
|
||||
m_vOffset = new Vector3(cos(ang) * radius, 0, sin(ang) * radius);
|
||||
m_vPos += m_vOffset;
|
||||
}
|
||||
```
|
||||
|
||||
## State Machine Details
|
||||
|
||||
### enumWait State
|
||||
- **Entry**: Event created
|
||||
- **Behavior**:
|
||||
- Increment `m_dwCurSpan`
|
||||
- Wait for `m_dwDelayTime`
|
||||
- **Exit**: When `m_dwCurSpan >= m_dwDelayTime`
|
||||
- **Transition**: → `enumFlying`
|
||||
|
||||
### enumFlying State
|
||||
- **Entry**: Delay expired
|
||||
- **Behavior**:
|
||||
- Call `m_pMoveMethod.SetMaxFlyTime(m_dwFlyTimeSpan)`
|
||||
- Call `m_pMoveMethod.StartMove(m_vHostPos, m_vTargetPos)`
|
||||
- Each frame:
|
||||
- Update host/target positions if `m_bTraceTarget`
|
||||
- Call `m_pMoveMethod.TickMove(dwDeltaTime, ...)`
|
||||
- Update GFX position
|
||||
- Check timeout: `m_dwCurSpan > m_dwFlyTimeSpan`
|
||||
- **Exit Conditions**:
|
||||
- `TickMove()` returns `true` (target hit)
|
||||
- `m_dwCurSpan > m_dwFlyTimeSpan` (timeout)
|
||||
- **Transition**: → `enumHit`
|
||||
|
||||
### enumHit State
|
||||
- **Entry**: Target hit or timeout
|
||||
- **Behavior**:
|
||||
- Release fly GFX (fade out or immediate)
|
||||
- Spawn hit GFX at target position
|
||||
- Update hit GFX position if `m_bTraceTarget`
|
||||
- Check if hit GFX is infinite (`m_bHitGfxInfinite`)
|
||||
- **Exit Conditions**:
|
||||
- Hit GFX completes (not infinite)
|
||||
- Hit GFX timeout (5 seconds for infinite)
|
||||
- Target disappears
|
||||
- **Transition**: → `enumFinished`
|
||||
|
||||
### enumFinished State
|
||||
- **Entry**: Hit effect complete
|
||||
- **Behavior**:
|
||||
- Release all GFX
|
||||
- Mark event for cleanup/pooling
|
||||
- **Exit**: Event removed from active list
|
||||
|
||||
## Position Tracking
|
||||
|
||||
### Host Position
|
||||
- **Source**: Character position from `m_nHostID`
|
||||
- **Update**: Each frame if `m_bHostExist`
|
||||
- **Usage**:
|
||||
- Initial position for `StartMove()`
|
||||
- Dynamic updates for `TickMove()` (if target moves)
|
||||
|
||||
### Target Position
|
||||
- **Source**: Character position from `m_nTargetID`
|
||||
- **Update**: Each frame if `m_bTargetExist` and `m_bTraceTarget`
|
||||
- **Usage**:
|
||||
- Target position for `StartMove()`
|
||||
- Dynamic updates for `TickMove()` (tracking)
|
||||
|
||||
### GetTargetCenter()
|
||||
- **Abstract method**: Must be implemented by derived class
|
||||
- **Purpose**: Get current target center position
|
||||
- **Implementation**:
|
||||
- `CECSkillGfxEvent`: Gets character position from ID
|
||||
- Can use hooks/bones in future
|
||||
|
||||
## GFX Loading & Instantiation
|
||||
|
||||
### C++ Pattern
|
||||
```cpp
|
||||
A3DGFXEx* LoadFlyGfx(A3DDevice* pDev, const char* szPath);
|
||||
void SetFlyGfx(A3DGFXEx* pFlyGfx);
|
||||
```
|
||||
|
||||
### C# Pattern (Unity)
|
||||
```csharp
|
||||
// Async loading with Addressables
|
||||
async Task<GameObject> LoadFlyGfxAsync(string path)
|
||||
{
|
||||
var handle = Addressables.LoadAssetAsync<GameObject>(path);
|
||||
await handle.Task;
|
||||
return handle.Result;
|
||||
}
|
||||
|
||||
// Instantiation
|
||||
GameObject instance = Instantiate(prefab, position, rotation);
|
||||
```
|
||||
|
||||
### Lifecycle
|
||||
1. **Load**: Async load prefab (Addressables)
|
||||
2. **Instantiate**: Create instance at position
|
||||
3. **Update**: Update position each frame
|
||||
4. **Release**: Destroy instance, release prefab
|
||||
|
||||
## Parameter System
|
||||
|
||||
### GFX_SKILL_PARAM Structure
|
||||
```csharp
|
||||
public struct GFX_SKILL_PARAM
|
||||
{
|
||||
public ValueUnion value; // Union: bVal, nVal, or fVal
|
||||
public bool m_bArea;
|
||||
public EmitShape m_Shape;
|
||||
public A3DVECTOR3 m_vSize;
|
||||
}
|
||||
```
|
||||
|
||||
### Usage by Movement Mode
|
||||
- **CGfxLinearMove**: Uses `m_bArea`, `m_Shape`, `m_vSize` (default)
|
||||
- **CGfxOnTargetMove**: Also uses `value.fVal` for radius
|
||||
- **CGfxMeteoricMove**: Uses `value.fVal` for fall radius
|
||||
- **CGfxHelixMove**: Uses `value.fVal` for spiral radius
|
||||
- **CGfxCurvedMove**: Uses `value.bVal` for curve direction
|
||||
- **CGfxAccMove**: Uses `value.fVal` for acceleration
|
||||
- **CGfxRandMove**: Uses `value.fVal` for speed
|
||||
|
||||
### Setting Parameters
|
||||
```csharp
|
||||
GFX_SKILL_PARAM param = new GFX_SKILL_PARAM();
|
||||
param.value.fVal = 5.0f; // Radius for OnTarget
|
||||
param.m_bArea = true;
|
||||
param.m_Shape = EmitShape.enumSphere;
|
||||
param.m_vSize = new A3DVECTOR3(2, 2, 2);
|
||||
|
||||
m_pMoveMethod.SetParam(param);
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Object Pooling
|
||||
- **Purpose**: Reuse event instances
|
||||
- **Implementation**: `m_FreeLst` in `CECSkillGfxMan`
|
||||
- **Usage**:
|
||||
- Get from pool when creating event
|
||||
- Return to pool when finished
|
||||
|
||||
### GFX Caching
|
||||
- **Purpose**: Avoid reloading same prefabs
|
||||
- **Implementation**: Cache loaded Addressable handles
|
||||
- **Key**: Use path as key
|
||||
|
||||
### Batch Updates
|
||||
- **Purpose**: Update all events efficiently
|
||||
- **Implementation**: Single loop through active events
|
||||
- **Optimization**: Early exit for finished events
|
||||
|
||||
### LOD System
|
||||
- **Purpose**: Reduce quality for distant effects
|
||||
- **Field**: `m_bGfxUseLod`
|
||||
- **Usage**: Disable expensive effects when far away
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Common Issues
|
||||
1. **Projectile doesn't move**: Check `StartMove()` called, `m_fSpeed` set
|
||||
2. **Projectile goes wrong direction**: Check `m_vMoveDir` calculation
|
||||
3. **Hit doesn't trigger**: Check `TickMove()` return value, timeout
|
||||
4. **GFX doesn't appear**: Check loading, instantiation, position
|
||||
5. **Performance issues**: Check pooling, caching, LOD
|
||||
|
||||
### Debug Fields
|
||||
- `m_vPos`: Current position
|
||||
- `m_vMoveDir`: Current direction
|
||||
- `m_enumState`: Current state
|
||||
- `m_dwCurSpan`: Current time span
|
||||
- `m_dwFlyTimeSpan`: Max fly time
|
||||
|
||||
### Visualization
|
||||
- Draw gizmos for projectile position
|
||||
- Draw line from host to target
|
||||
- Show state in inspector
|
||||
- Log state transitions
|
||||
@@ -0,0 +1,45 @@
|
||||
# Agent Skills for Perfect World Unity C++ to C# Conversion
|
||||
|
||||
This folder contains comprehensive skills and guidelines for AI agents working on the Perfect World Unity C++ to C# conversion project.
|
||||
|
||||
## Skills Index
|
||||
|
||||
### Core Conversion Skills
|
||||
1. **[Naming Conventions](./01-naming-conventions.md)** - Maintain C++ naming exactly (CGfxMoveBase, m_bOneOfCluser, etc.)
|
||||
2. **[Type Mappings](./02-type-mappings.md)** - C++ to C# type conversions (DWORD→uint, A3DVECTOR3→Vector3, etc.)
|
||||
3. **[Code Patterns](./03-code-patterns.md)** - Common C++ patterns and their C# equivalents
|
||||
|
||||
### Architecture & Understanding
|
||||
4. **[Architecture Understanding](./04-architecture-understanding.md)** - System structure, flow, and relationships
|
||||
5. **[Skill GFX System Deep Dive](./05-skill-gfx-deep-dive.md)** - Detailed GFX system architecture
|
||||
|
||||
### Quality & Validation
|
||||
6. **[Testing & Validation](./06-testing-validation.md)** - How to verify conversions are correct
|
||||
7. **[Common Pitfalls](./07-common-pitfalls.md)** - Mistakes to avoid during conversion
|
||||
8. **[Best Practices](./08-best-practices.md)** - Recommended approaches and patterns
|
||||
|
||||
### Reference
|
||||
9. **[C++ Source Reference](./09-cpp-source-reference.md)** - Key C++ files and their locations
|
||||
10. **[Unity C# Reference](./10-unity-csharp-reference.md)** - Key Unity C# files and their locations
|
||||
|
||||
## Quick Start
|
||||
|
||||
When starting a conversion task:
|
||||
1. Read [Naming Conventions](./01-naming-conventions.md) - **CRITICAL** - Never change C++ names unnecessarily
|
||||
2. Check [Type Mappings](./02-type-mappings.md) for type conversions
|
||||
3. Review [Common Pitfalls](./07-common-pitfalls.md) before coding
|
||||
4. Use [Testing & Validation](./06-testing-validation.md) to verify your work
|
||||
|
||||
## Priority Rules
|
||||
|
||||
1. **Preserve C++ naming** - If C++ has `CGfxMoveBase`, use `CGfxMoveBase` (not `IGfxMovement`)
|
||||
2. **Match C++ behavior exactly** - Don't "improve" logic unless explicitly requested
|
||||
3. **Maintain field names** - Even typos like `m_bOneOfCluser` must be preserved
|
||||
4. **Verify against C++ source** - Always check the actual C++ implementation
|
||||
|
||||
## Project Context
|
||||
|
||||
- **Source:** `perfect-world-source/` - Original C++ codebase
|
||||
- **Target:** `perfect-world-unity/Assets/` - Unity C# project
|
||||
- **Main Plan:** `SKILL_GFX_CONVERSION_PLAN.md` - Overall conversion strategy
|
||||
- **Quick Guide:** `SKILL_GFX_QUICK_START.md` - Fast implementation guide
|
||||
Reference in New Issue
Block a user