From 3f1cf2fe86ddc5f4fab3b9b018a07a0b1cfff190 Mon Sep 17 00:00:00 2001 From: reignsprime Date: Tue, 7 Apr 2026 19:12:14 +1000 Subject: [PATCH 1/7] Initial commit - displaying capes --- src/Client/WarFare/GameProcMain.cpp | 22 +++------ src/Client/WarFare/PlayerBase.cpp | 67 +++++++++++++++++++++++-- src/Client/WarFare/PlayerBase.h | 1 + src/Client/WarFare/PlayerMySelf.cpp | 6 +-- src/Client/WarFare/PlayerOther.cpp | 6 ++- src/N3Base/N3Chr.cpp | 76 +++++++++++++++++++++++++++-- src/N3Base/N3Chr.h | 25 ++++++++-- src/N3Base/N3Cloak.cpp | 49 ++++++++++++------- src/N3Base/N3Cloak.h | 5 +- 9 files changed, 203 insertions(+), 54 deletions(-) diff --git a/src/Client/WarFare/GameProcMain.cpp b/src/Client/WarFare/GameProcMain.cpp index 76a620e26..67858da9f 100644 --- a/src/Client/WarFare/GameProcMain.cpp +++ b/src/Client/WarFare/GameProcMain.cpp @@ -1890,17 +1890,18 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) /*int iAllianceID =*/pkt.read(); /*uint8_t byFlag =*/pkt.read(); - int iKnightNameLen = pkt.read(); // 소속 기사단 이름 길이. + int iKnightNameLen = pkt.read(); // 소속 기사단 이름 길이. pkt.readString(szKnightsName, iKnightNameLen); - int iKnightsGrade = pkt.read(); // 소속 기사단 등급 - int iKnightsRank = pkt.read(); // 소속 기사단 순위 + int iKnightsGrade = pkt.read(); // 소속 기사단 등급 + int iKnightsRank = pkt.read(); // 소속 기사단 순위 /*int16_t sMarkVersion =*/pkt.read(); - /*int16_t sCapeID =*/pkt.read(); + int16_t sCapeID = pkt.read(); // Clan cape // 기사단 관련 세팅.. s_pPlayer->m_InfoExt.eKnightsDuty = eKnightsDuty; // 기사단에서의 권한.. s_pPlayer->KnightsInfoSet(iKnightsID, szKnightsName, iKnightsGrade, iKnightsRank); + s_pPlayer->AttachCloak(sCapeID); m_pUIVar->UpdateKnightsInfo(); s_pPlayer->m_InfoBase.iHPMax = pkt.read(); @@ -2178,14 +2179,6 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) InitPlayerPosition(__Vector3(fX, fY, fZ)); // 플레이어 위치 초기화.. 일으켜 세우고, 기본동작을 취하게 한다. s_pPlayer->RegenerateCollisionMesh(); - // berserk temp - //s_pPlayer->PlugSet(PLUG_POS_BACK, "item/babacloak.n3cplug_cloak", nullptr); // 파트를 셋팅.. - // end berserk temp - - // berserk - //s_pPlayer->AttachCloak(); - - //.. s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. if (m_pUICmdList != nullptr) @@ -2549,9 +2542,9 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) int iKnightsRank = pkt.read(); // 순위 /*int16_t sMarkVersion =*/pkt.read(); - /*int16_t sCapeID =*/pkt.read(); + int16_t sCapeID = pkt.read(); // Clan cape - int iLevel = pkt.read(); // 레벨... + int iLevel = pkt.read(); // 레벨... e_Race eRace = (e_Race) pkt.read(); e_Class eClass = (e_Class) pkt.read(); float fXPos = (pkt.read()) / 10.0f; @@ -2632,6 +2625,7 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) pUPC->Init(eRace, iFace, iHair, dwItemIDs, iItemDurabilities, byItemFlags); pUPC->RotateTo(DegreesToRadians(rand() % 360), true); pUPC->KnightsInfoSet(iKnightsID, szKnightsName, iKnightsGrade, iKnightsRank); + pUPC->AttachCloak(sCapeID); //__KnightsInfoBase* pKIB = m_pUIKnightsOp->KnightsInfoFind(iKightsID); //if(pKIB) pUPC->KnightsNameSet(pKIB->szName, 0xffff0000); diff --git a/src/Client/WarFare/PlayerBase.cpp b/src/Client/WarFare/PlayerBase.cpp index f004cb020..8406ae91c 100644 --- a/src/Client/WarFare/PlayerBase.cpp +++ b/src/Client/WarFare/PlayerBase.cpp @@ -528,12 +528,24 @@ void CPlayerBase::TickAnimation() // 걷고 뛰고, 에니메이션등... 속도 적용 float fAniSpeedDelta = 1.0f; if (PSM_STOP != m_eStateMove) - fAniSpeedDelta = m_fMoveDelta; // 이동중이면 스피드 적용.. + fAniSpeedDelta = m_fMoveDelta; // 이동중이면 스피드 적용.. else if (PSA_ATTACK == m_eState) - fAniSpeedDelta = m_fAttackDelta; // 공격중이면 공격 스피드 적용.. + fAniSpeedDelta = m_fAttackDelta; // 공격중이면 공격 스피드 적용.. __ASSERT(fAniSpeedDelta >= 0.1f && fAniSpeedDelta < 10.0f, "Invalid Animation Speed Delta!!!"); - m_Chr.AniSpeedDeltaSet(fAniSpeedDelta); // 에니메이션 스피드 실제 적용.. - m_Chr.Tick(); // 에니메이션 틱.. + m_Chr.AniSpeedDeltaSet(fAniSpeedDelta); // 에니메이션 스피드 실제 적용.. + m_Chr.Tick(); // 에니메이션 틱.. + + if (m_Chr.CloakPlug() != nullptr) + { + e_CloakMove eCloakMove = CLOAK_MOVE_STOP; + if (m_eStateMove == PSM_RUN) + eCloakMove = CLOAK_MOVE_RUN; + else if (m_eStateMove == PSM_WALK) + eCloakMove = CLOAK_MOVE_WALK; + else if (m_eStateMove == PSM_WALK_BACKWARD) + eCloakMove = CLOAK_MOVE_WALK_BACKWARD; + m_Chr.CloakPlug()->GetCloak()->Tick(m_Chr.m_nLOD, m_fYawCur, eCloakMove); + } m_bAnimationChanged = false; // 큐에 넣은 에니메이션이 변하는 순간만 세팅된다.. if (m_Chr.IsAnimEnd()) // 에니메이션이 끝나면.. @@ -1736,7 +1748,14 @@ CN3CPlugBase* CPlayerBase::PlugSet(e_PlugPosition ePos, const std::string& szFN, } else if (PLUG_POS_BACK == ePos) { - //m_pItemPlugBasics[PLUG_POS_BACK] = pItem; + m_pItemPlugBasics[ePos] = pItemBasic; + m_pItemPlugExts[ePos] = pItemExt; + + CN3CPlug_Cloak* pCloak = m_Chr.CloakPlugSet(szFN); + if (pCloak != nullptr) + pCloak->m_nJointIndex = ITEM_POS_SHOULDER; + + return pCloak; } else { @@ -1849,6 +1868,44 @@ CN3CPlugBase* CPlayerBase::PlugSet(e_PlugPosition ePos, const std::string& szFN, return pPlug; } +void CPlayerBase::AttachCloak(int16_t sCapeID) +{ + m_Chr.CloakPlugSet(""); + + // -1 = no clan, 0 = no cape + if (sCapeID <= 0) + return; + + // Cloak mesh for each race + const std::string szMesh = fmt::format("Item\\cloak_{:03}.n3cplug", static_cast(m_InfoBase.eRace)); + CN3CPlug_Cloak* pCloak = m_Chr.CloakPlugSet(szMesh); + if (pCloak == nullptr) + return; + + // Attach cloak to shoulders + pCloak->m_nJointIndex = ITEM_POS_SHOULDER; + + // Cloak colour and pattern + int nPattern = sCapeID / 100; + int nColour = sCapeID % 100; + + std::string szColour; + if (nColour > 0) + szColour = fmt::format("Item\\cloak_c_{:02}.dxt", nColour); + else + szColour = "Item\\cloak_basic.dxt"; + + if (pCloak->TexSet(szColour) == nullptr) + pCloak->TexSet("Item\\cloak_basic.dxt"); + + if (nPattern >= 1 && nPattern <= 5) + pCloak->TexOverlapSet(fmt::format("Item\\cloak_M_{:02}.dxt", nPattern)); + else + pCloak->TexOverlapSet(static_cast(nullptr)); + + pCloak->GetCloak()->Init(pCloak); +} + CN3CPart* CPlayerBase::PartSet(e_PartPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) { if (ePos < PART_POS_UPPER || ePos >= PART_POS_COUNT) diff --git a/src/Client/WarFare/PlayerBase.h b/src/Client/WarFare/PlayerBase.h index 5464ca35f..59edf13a7 100644 --- a/src/Client/WarFare/PlayerBase.h +++ b/src/Client/WarFare/PlayerBase.h @@ -414,6 +414,7 @@ class CPlayerBase : public CGameBase virtual CN3CPart* PartSet(e_PartPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt); virtual CN3CPlugBase* PlugSet(e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt); virtual void DurabilitySet(e_ItemSlot eSlot, int iDurability); + void AttachCloak(int16_t sCapeID); void TickYaw(); // 회전값 처리. void TickAnimation(); // 에니메이션 처리. diff --git a/src/Client/WarFare/PlayerMySelf.cpp b/src/Client/WarFare/PlayerMySelf.cpp index 3cf30ce04..49b5af295 100644 --- a/src/Client/WarFare/PlayerMySelf.cpp +++ b/src/Client/WarFare/PlayerMySelf.cpp @@ -595,7 +595,7 @@ CN3CPlugBase* CPlayerMySelf::PlugSet( } else if (PLUG_POS_BACK == ePos) { - //m_pItemBasicPlugRefs[PLUG_POS_BACK] = pItem; + return CPlayerBase::PlugSet(ePos, szFN, pItemBasic, pItemExt); } else { @@ -613,10 +613,6 @@ CN3CPlugBase* CPlayerMySelf::PlugSet( pPlug->ScaleSet(__Vector3(fScale, fScale, fScale)); pPlug->m_nJointIndex = iJoint; // 관절 번호 세팅.. } - // else if(PLUG_POS_BACK == ePos) - // { - // CN3CPlug_Cloak *pPlugCloak = (CN3CPlug_Cloak*)pPlug; - // } this->SetSoundPlug(pItemBasic); return CPlayerBase::PlugSet(ePos, szFN, pItemBasic, pItemExt); diff --git a/src/Client/WarFare/PlayerOther.cpp b/src/Client/WarFare/PlayerOther.cpp index 4e776c7ee..55177ae0f 100644 --- a/src/Client/WarFare/PlayerOther.cpp +++ b/src/Client/WarFare/PlayerOther.cpp @@ -130,9 +130,11 @@ bool CPlayerOther::Init(e_Race eRace, int iFace, int iHair, uint32_t* pdwItemIDs ePart = PART_POS_FEET; eSlot = ITEM_SLOT_SHOES; } - else if (5 == i) + else if (5 == i) // Clan cape { - } // 망토 + ePlug = PLUG_POS_BACK; + eSlot = ITEM_SLOT_SHOULDER; + } else if (6 == i) { ePlug = PLUG_POS_RIGHTHAND; diff --git a/src/N3Base/N3Chr.cpp b/src/N3Base/N3Chr.cpp index 97567221b..45d44e9c7 100644 --- a/src/N3Base/N3Chr.cpp +++ b/src/N3Base/N3Chr.cpp @@ -1047,16 +1047,59 @@ CN3CPlug_Cloak::~CN3CPlug_Cloak() void CN3CPlug_Cloak::Release() { + s_MngMesh.Delete(&m_pMesh); CN3CPlugBase::Release(); } bool CN3CPlug_Cloak::Load(File& file) { - CN3CPlugBase::Load(file); + // Read the name header (CN3BaseFileAccess::Load), then re-parse all + // CN3CPlugBase fields manually so we can redirect the mesh load to + // s_MngMesh (CN3Mesh) instead of PMeshSet (CN3PMesh). Cloak assets are + // .n3mesh files; loading them as .n3pmesh produces garbage vertex counts. + CN3BaseFileAccess::Load(file); + + int nL = 0; + char szFN[512] = ""; + + file.Read(&m_ePlugType, 4); + if (m_ePlugType > PLUGTYPE_MAX) + m_ePlugType = PLUGTYPE_NORMAL; + file.Read(&m_nJointIndex, 4); + file.Read(&m_vPosition, sizeof(m_vPosition)); + file.Read(&m_MtxRot, sizeof(m_MtxRot)); + file.Read(&m_vScale, sizeof(m_vScale)); + file.Read(&m_Mtl, sizeof(__Material)); + + // Mesh: load as CN3Mesh, not CN3PMesh + file.Read(&nL, 4); + if (nL > 0) + { + file.Read(szFN, nL); + szFN[nL] = '\0'; + s_MngMesh.Delete(&m_pMesh); + m_pMesh = s_MngMesh.Get(szFN); + } + + // Texture + file.Read(&nL, 4); + if (nL > 0) + { + file.Read(szFN, nL); + szFN[nL] = '\0'; + TexSet(szFN); + } + + ReCalcMatrix(); + + if (m_pMesh == nullptr) + return false; // mesh file missing; CloakPlugSet will clean up + if (Tex() == nullptr) + TexSet("Item\\cloak_basic.dxt"); #ifdef _N3GAME m_Cloak.Init(this); #endif - return 0; + return true; } #ifdef _N3TOOL @@ -1076,7 +1119,7 @@ void CN3CPlug_Cloak::Render(const __Matrix44& mtxParent, const __Matrix44& mtxJo mtx = m_Matrix; mtx *= mtxJoint; mtx *= mtxParent; - m_Cloak.Render(mtx); + m_Cloak.Render(mtx, TexOverlap()); #endif } @@ -1123,6 +1166,9 @@ CN3Chr::~CN3Chr() delete *itr; m_Plugs.clear(); + delete m_pCloakPlug; + m_pCloakPlug = nullptr; + for (auto itr = m_vTraces.begin(); itr != m_vTraces.end(); ++itr) delete *itr; m_vTraces.clear(); @@ -1892,6 +1938,12 @@ void CN3Chr::Render() //////////////////////////////////////////////////// } + // Clan cape render + if (m_pCloakPlug != nullptr && m_pCloakPlug->m_bVisible && m_pCloakPlug->m_nJointIndex >= 0) + { + m_pCloakPlug->Render(m_Matrix, m_MtxJoints[m_pCloakPlug->m_nJointIndex]); + } + ////////////////////////////////////////////////// // Coded (By Dino On 2002-10-11 오전 11:20:19 ) // FXPlug @@ -2201,6 +2253,24 @@ void CN3Chr::PlugAlloc(int iCount) } } +CN3CPlug_Cloak* CN3Chr::CloakPlugSet(const std::string& szFN) +{ + delete m_pCloakPlug; + m_pCloakPlug = nullptr; + + if (szFN.empty()) + return nullptr; + + m_pCloakPlug = new CN3CPlug_Cloak(); + if (!m_pCloakPlug->LoadFromFile(szFN)) + { + delete m_pCloakPlug; + m_pCloakPlug = nullptr; + return nullptr; + } + return m_pCloakPlug; +} + /* void CN3Chr::CollisionSkinSet(const std::string& szFN) { diff --git a/src/N3Base/N3Chr.h b/src/N3Base/N3Chr.h index f2fc70a91..4c7dcaeb5 100644 --- a/src/N3Base/N3Chr.h +++ b/src/N3Base/N3Chr.h @@ -12,6 +12,7 @@ #include "N3Skin.h" #include "N3Cloak.h" #include "N3PMeshInstance.h" +#include "N3Mesh.h" #include "N3Texture.h" #include "N3Joint.h" #include @@ -293,6 +294,13 @@ class CN3CPlug_Cloak : public CN3CPlugBase #ifdef _N3GAME CN3Cloak m_Cloak; #endif + CN3Mesh* m_pMesh = nullptr; + +public: + CN3Mesh* Mesh() + { + return m_pMesh; + } }; // 0 - 상체, 1 - 하체 ::: 관절들을 나누어서 나누어서 에니메이션 설정.. @@ -327,12 +335,13 @@ class CN3Chr : public CN3TransformCollision // 각 조인트의 행렬.. 포인터로 두지 않은 이유는 각 캐릭터마다 따로 에니메이션이 되기 위함이다.. std::vector<__Matrix44> m_MtxJoints; - std::vector<__Matrix44> m_MtxInverses; // 조인트에 대한 역행렬 + std::vector<__Matrix44> m_MtxInverses; // 조인트에 대한 역행렬 - std::vector m_Parts; // 각 캐릭터의 부분별 Data Pointer List - std::vector m_Plugs; // 이 캐릭터에 붙이는 무기등의 Data Pointer List - std::vector<__VertexColor*> m_vTraces; // Plug Trace Polygon - class CN3FXPlug* m_pFXPlug; // 캐릭터에 FX를 붙이는 것. + std::vector m_Parts; // 각 캐릭터의 부분별 Data Pointer List + std::vector m_Plugs; // 이 캐릭터에 붙이는 무기등의 Data Pointer List + std::vector<__VertexColor*> m_vTraces; // Plug Trace Polygon + class CN3FXPlug* m_pFXPlug; // 캐릭터에 FX를 붙이는 것. + CN3CPlug_Cloak* m_pCloakPlug = nullptr; // Clan cape attachment // 조인트의 일부분이 따로 에니메이션 되야 한다면.. 조인트 인덱스 시작 번호 int m_nJointPartStarts[MAX_CHR_ANI_PART]; @@ -471,6 +480,12 @@ class CN3Chr : public CN3TransformCollision return m_Plugs[iIndex]; } + CN3CPlug_Cloak* CloakPlugSet(const std::string& szFN); + CN3CPlug_Cloak* CloakPlug() + { + return m_pCloakPlug; + } + void Tick(float fFrm = FRAME_SELFPLAY) override; void TickAnimationFrame(); void TickJoints(); diff --git a/src/N3Base/N3Cloak.cpp b/src/N3Base/N3Cloak.cpp index e7cf887c2..67674d5f8 100644 --- a/src/N3Base/N3Cloak.cpp +++ b/src/N3Base/N3Cloak.cpp @@ -5,7 +5,6 @@ #include "StdAfxBase.h" #include "N3Cloak.h" #include "N3Texture.h" -#include "N3PMeshInstance.h" #include "N3Chr.h" CN3Cloak::CN3Cloak() @@ -14,7 +13,7 @@ CN3Cloak::CN3Cloak() m_pTex = nullptr; m_pParticle = nullptr; m_nLOD = -1; - m_pPMesh = nullptr; + m_pMesh = nullptr; m_pIndex = nullptr; m_pVertex = nullptr; m_fOffsetRecoveryTime = 0.0f; @@ -52,9 +51,9 @@ void CN3Cloak::Release() void CN3Cloak::Init(CN3CPlug_Cloak* pPlugCloak) { - m_pTex = pPlugCloak->Tex(); - m_pPMesh = pPlugCloak->PMesh(); - __ASSERT(m_pPMesh && m_pTex, "IN CN3Cloak, PMesh or m_pTex is null"); + m_pTex = pPlugCloak->Tex(); + m_pMesh = pPlugCloak->Mesh(); + __ASSERT(m_pMesh && m_pTex, "IN CN3Cloak, Mesh or m_pTex is null"); SetLOD(0); @@ -104,9 +103,9 @@ void CN3Cloak::Tick(int /*nLOD*/, float fYaw, e_CloakMove eCloakMove) m_Force.x = m_Force.y = m_Force.z = 0.0f; } -void CN3Cloak::Render(__Matrix44& mtx) +void CN3Cloak::Render(__Matrix44& mtx, CN3Texture* pTexColour) { - if (m_nLOD < 0) + if (m_nLOD < 0 || m_pTex == nullptr) return; s_lpD3DDev->SetTransform(D3DTS_WORLD, mtx.toD3D()); @@ -121,22 +120,33 @@ void CN3Cloak::Render(__Matrix44& mtx) s_lpD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); s_lpD3DDev->SetTexture(0, m_pTex->Get()); - s_lpD3DDev->SetTexture(1, nullptr); s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); - // s_lpD3DDev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - // s_lpD3DDev->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - // s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + + if (pTexColour != nullptr) + { + s_lpD3DDev->SetTexture(1, pTexColour->Get()); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); + } + else + { + s_lpD3DDev->SetTexture(1, nullptr); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + } s_lpD3DDev->SetFVF(FVF_VNT1); s_lpD3DDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, m_nVertexCount, m_nIndexCount / 3, m_pIndex, D3DFMT_INDEX16, m_pVertex, sizeof(__VertexT1)); - // + + /* Grid for debugging the clan cape movement __VertexXyzColor Vtx[2] {}; - /* __VertexT1 *pTemp = m_pVertex; Vtx[0].Set(pTemp->x, pTemp->y, pTemp->z, 0xffffffff); Vtx[1].Set(1.0f, 1.0f, 1.0f, 0xffffffff); @@ -147,7 +157,7 @@ void CN3Cloak::Render(__Matrix44& mtx) Vtx[1].Set(1.0f, 1.0f, 1.0f, 0xffffffff); s_lpD3DDev->SetVertexShader(FVF_CV); s_lpD3DDev->DrawPrimitiveUP(D3DPT_LINELIST, 1, Vtx, sizeof(__VertexXyzColor)); -*/ + for (int i = 0; i < m_nGridH; i++) { @@ -179,8 +189,11 @@ void CN3Cloak::Render(__Matrix44& mtx) s_lpD3DDev->DrawPrimitiveUP(D3DPT_LINELIST, 1, Vtx, sizeof(__VertexXyzColor)); } } + */ // restore renderstate. + s_lpD3DDev->SetTexture(1, nullptr); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); if (dwCull != D3DCULL_NONE) s_lpD3DDev->SetRenderState(D3DRS_CULLMODE, dwCull); } @@ -335,12 +348,12 @@ void CN3Cloak::SetLOD(int nLevel) m_nGridW = 7; m_nGridH = 5; - int nVertexCount = m_pPMesh->GetMaxNumVertices(); - int nIndexCount = m_pPMesh->GetMaxNumIndices(); + int nVertexCount = m_pMesh->VertexCount(); + int nIndexCount = m_pMesh->IndexCount(); m_pVertex = new __VertexT1[nVertexCount]; - memcpy(&m_pVertex, m_pPMesh->GetVertices(), sizeof(__VertexT1) * nVertexCount); + memcpy(m_pVertex, m_pMesh->Vertices(), sizeof(__VertexT1) * nVertexCount); m_pIndex = new uint16_t[nIndexCount]; - memcpy(m_pIndex, m_pPMesh->GetIndices(), sizeof(uint16_t) * nIndexCount); + memcpy(m_pIndex, m_pMesh->Indices(), sizeof(uint16_t) * nIndexCount); m_nVertexCount = nVertexCount; m_nIndexCount = nIndexCount; // diff --git a/src/N3Base/N3Cloak.h b/src/N3Base/N3Cloak.h index ee79f334e..a0359e926 100644 --- a/src/N3Base/N3Cloak.h +++ b/src/N3Base/N3Cloak.h @@ -8,6 +8,7 @@ #pragma once #include "N3Base.h" +#include "N3Mesh.h" inline constexpr int CLOAK_MAX_WIDTH = 7; inline constexpr int CLOAK_MAX_HEIGHT = 7; @@ -93,7 +94,7 @@ class CN3Cloak : public CN3Base int m_nGridW, m_nGridH; int m_nLOD; - CN3PMesh* m_pPMesh; + CN3Mesh* m_pMesh; float m_fOffsetRecoveryTime; float m_fPrevYaw; @@ -108,7 +109,7 @@ class CN3Cloak : public CN3Base public: virtual void Tick(int nLOD, float fYaw, e_CloakMove eCurMove); - virtual void Render(__Matrix44& mtx); + virtual void Render(__Matrix44& mtx, CN3Texture* pTexColour = nullptr); void Release() override; }; From c0333bbc3ea1df4ef1c831b5f244a0b369ee7d58 Mon Sep 17 00:00:00 2001 From: reignsprime Date: Sat, 11 Apr 2026 15:08:24 +1000 Subject: [PATCH 2/7] Implement CGameProcedure::GetSymbolFilename Bit of a clean up to align more with binary --- src/Client/WarFare/GameProcMain.cpp | 44 +++++------ src/Client/WarFare/GameProcedure.cpp | 5 ++ src/Client/WarFare/GameProcedure.h | 2 + src/Client/WarFare/PlayerBase.cpp | 51 ++++++------ src/Client/WarFare/PlayerBase.h | 17 ++-- src/Client/WarFare/PlayerMySelf.cpp | 14 +++- src/Client/WarFare/PlayerMySelf.h | 4 +- src/N3Base/N3Chr.cpp | 56 ++++++++++++-- src/N3Base/N3Chr.h | 9 ++- src/N3Base/N3Cloak.cpp | 111 ++++++++++++++++++++------- src/N3Base/N3Cloak.h | 20 ++++- 11 files changed, 234 insertions(+), 99 deletions(-) diff --git a/src/Client/WarFare/GameProcMain.cpp b/src/Client/WarFare/GameProcMain.cpp index 67858da9f..3c02b4d2e 100644 --- a/src/Client/WarFare/GameProcMain.cpp +++ b/src/Client/WarFare/GameProcMain.cpp @@ -1890,18 +1890,18 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) /*int iAllianceID =*/pkt.read(); /*uint8_t byFlag =*/pkt.read(); - int iKnightNameLen = pkt.read(); // 소속 기사단 이름 길이. + int iKnightNameLen = pkt.read(); // 소속 기사단 이름 길이. pkt.readString(szKnightsName, iKnightNameLen); - int iKnightsGrade = pkt.read(); // 소속 기사단 등급 - int iKnightsRank = pkt.read(); // 소속 기사단 순위 + int iKnightsGrade = pkt.read(); // 소속 기사단 등급 + int iKnightsRank = pkt.read(); // 소속 기사단 순위 - /*int16_t sMarkVersion =*/pkt.read(); - int16_t sCapeID = pkt.read(); // Clan cape + int16_t sMarkVersion = pkt.read(); // Clan mark + int16_t sCapeID = pkt.read(); // Clan cloak // 기사단 관련 세팅.. - s_pPlayer->m_InfoExt.eKnightsDuty = eKnightsDuty; // 기사단에서의 권한.. + s_pPlayer->m_InfoExt.eKnightsDuty = eKnightsDuty; // 기사단에서의 권한.. + s_pPlayer->m_InfoBase.iMarkVersion = sMarkVersion; // Clan mark s_pPlayer->KnightsInfoSet(iKnightsID, szKnightsName, iKnightsGrade, iKnightsRank); - s_pPlayer->AttachCloak(sCapeID); m_pUIVar->UpdateKnightsInfo(); s_pPlayer->m_InfoBase.iHPMax = pkt.read(); @@ -2178,8 +2178,8 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) InitPlayerPosition(__Vector3(fX, fY, fZ)); // 플레이어 위치 초기화.. 일으켜 세우고, 기본동작을 취하게 한다. s_pPlayer->RegenerateCollisionMesh(); - - s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. + s_pPlayer->AttachCloak(sCapeID); // Cloak + s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. if (m_pUICmdList != nullptr) m_pUICmdList->CreateCategoryList(); @@ -2535,24 +2535,24 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) /*int16_t sAllianceID =*/pkt.read(); - int iKnightNameLen = pkt.read(); // 소속 기사단 이름 길이. + int iKnightNameLen = pkt.read(); // 소속 기사단 이름 길이. std::string szKnightsName; pkt.readString(szKnightsName, iKnightNameLen); - int iKnightsGrade = pkt.read(); // 등급 - int iKnightsRank = pkt.read(); // 순위 + int iKnightsGrade = pkt.read(); // 등급 + int iKnightsRank = pkt.read(); // 순위 - /*int16_t sMarkVersion =*/pkt.read(); - int16_t sCapeID = pkt.read(); // Clan cape + int16_t sMarkVersion = pkt.read(); // Clan mark + int16_t sCapeID = pkt.read(); // Clan cloak - int iLevel = pkt.read(); // 레벨... - e_Race eRace = (e_Race) pkt.read(); - e_Class eClass = (e_Class) pkt.read(); - float fXPos = (pkt.read()) / 10.0f; - float fZPos = (pkt.read()) / 10.0f; - float fYPos = (pkt.read()) / 10.0f; + int iLevel = pkt.read(); // 레벨... + e_Race eRace = (e_Race) pkt.read(); + e_Class eClass = (e_Class) pkt.read(); + float fXPos = (pkt.read()) / 10.0f; + float fZPos = (pkt.read()) / 10.0f; + float fYPos = (pkt.read()) / 10.0f; - float fYTerrain = ACT_WORLD->GetHeightWithTerrain(fXPos, fZPos); // 지형의 높이값 얻기.. - float fYObject = ACT_WORLD->GetHeightNearstPosWithShape(__Vector3(fXPos, fYPos, fZPos)); // 오브젝트에서 가장 가까운 높이값 얻기.. + float fYTerrain = ACT_WORLD->GetHeightWithTerrain(fXPos, fZPos); // 지형의 높이값 얻기.. + float fYObject = ACT_WORLD->GetHeightNearstPosWithShape(__Vector3(fXPos, fYPos, fZPos)); // 오브젝트에서 가장 가까운 높이값 얻기.. if (fYObject > fYTerrain) fYPos = fYObject; else diff --git a/src/Client/WarFare/GameProcedure.cpp b/src/Client/WarFare/GameProcedure.cpp index b0b5d5af0..1f954122c 100644 --- a/src/Client/WarFare/GameProcedure.cpp +++ b/src/Client/WarFare/GameProcedure.cpp @@ -711,6 +711,11 @@ std::string CGameProcedure::GetStrRegKeySetting() return fmt::format("Software\\KnightOnline\\{}_{}_{}", s_szAccount, s_szServer, s_iChrSelectIndex); } +std::string CGameProcedure::GetSymbolFilename(const int serverIndex, const int knightsId, const int markVersion) +{ + return fmt::format("symbol_us\\s_{}_{}_{}.dxt", serverIndex, knightsId, markVersion); +} + bool CGameProcedure::ProcessPacket(Packet& pkt) { int iCmd = pkt.read(); // 커멘드 파싱.. diff --git a/src/Client/WarFare/GameProcedure.h b/src/Client/WarFare/GameProcedure.h index e6e20471a..44d01e32f 100644 --- a/src/Client/WarFare/GameProcedure.h +++ b/src/Client/WarFare/GameProcedure.h @@ -149,6 +149,8 @@ class CGameProcedure : public CGameBase static void SetGameCursor(e_Cursor eCursor, bool bLocked = false); static void RestoreGameCursor(); + static std::string GetSymbolFilename(const int serverIndex, const int knightsId, const int markVersion); + protected: virtual bool ProcessPacket(Packet& pkt); void MsgRecv_ServerChange(Packet& pkt); diff --git a/src/Client/WarFare/PlayerBase.cpp b/src/Client/WarFare/PlayerBase.cpp index 8406ae91c..b737b1f97 100644 --- a/src/Client/WarFare/PlayerBase.cpp +++ b/src/Client/WarFare/PlayerBase.cpp @@ -304,9 +304,10 @@ void CPlayerBase::KnightsInfoSet(int iID, const std::string& /*szName*/, int iGr szPlug = fmt::format("Item\\ClanAddOn_{:03}_{}.n3cplug", static_cast(m_InfoBase.eRace), iGrade); } - m_InfoBase.iKnightsID = iID; + m_InfoBase.iKnightsID = iID; + m_InfoBase.iKnightsGrade = iGrade; - CN3CPlugBase* pPlug = PlugSet(PLUG_POS_KNIGHTS_GRADE, szPlug, nullptr, nullptr); + CN3CPlugBase* pPlug = PlugSet(PLUG_POS_KNIGHTS_GRADE, szPlug, nullptr, nullptr); if (pPlug == nullptr) return; @@ -1714,7 +1715,8 @@ bool CPlayerBase::CheckCollisionToTargetByPlug(CPlayerBase* pTarget, int nPlug, return pTarget->CheckCollisionByBox(v1, v2, pVCol, nullptr); } -CN3CPlugBase* CPlayerBase::PlugSet(e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) +CN3CPlugBase* CPlayerBase::PlugSet( + e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, bool isForce) { if (ePos < PLUG_POS_RIGHTHAND || ePos >= PLUG_POS_COUNT) { @@ -1868,42 +1870,35 @@ CN3CPlugBase* CPlayerBase::PlugSet(e_PlugPosition ePos, const std::string& szFN, return pPlug; } -void CPlayerBase::AttachCloak(int16_t sCapeID) +void CPlayerBase::AttachCloak(int16_t sCapeID, bool isForce) { - m_Chr.CloakPlugSet(""); + PlugSet(PLUG_POS_BACK, "", nullptr, nullptr, isForce); - // -1 = no clan, 0 = no cape - if (sCapeID <= 0) + //Temporary to prevent cloaks on all chars while still a work in progress -1 = no cloak + if (sCapeID < 0) return; - // Cloak mesh for each race - const std::string szMesh = fmt::format("Item\\cloak_{:03}.n3cplug", static_cast(m_InfoBase.eRace)); - CN3CPlug_Cloak* pCloak = m_Chr.CloakPlugSet(szMesh); + const std::string sMeshPath = fmt::format("Item\\Cloak_{:03}.n3cplug", static_cast(m_InfoBase.eRace)); // Cloak mesh for each race + CN3CPlug_Cloak* pCloak = static_cast(PlugSet(PLUG_POS_BACK, sMeshPath, nullptr, nullptr, isForce)); if (pCloak == nullptr) return; - // Attach cloak to shoulders - pCloak->m_nJointIndex = ITEM_POS_SHOULDER; + // TODO: Implement king cloak, need to look at binary client some more - // Cloak colour and pattern - int nPattern = sCapeID / 100; - int nColour = sCapeID % 100; + int nColour = sCapeID % 100; + int nPattern = sCapeID / 100; - std::string szColour; - if (nColour > 0) - szColour = fmt::format("Item\\cloak_c_{:02}.dxt", nColour); - else - szColour = "Item\\cloak_basic.dxt"; - - if (pCloak->TexSet(szColour) == nullptr) - pCloak->TexSet("Item\\cloak_basic.dxt"); + std::string SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour + std::string sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern + std::string sClanMarkTexPath = ""; // No Clan Mark - if (nPattern >= 1 && nPattern <= 5) - pCloak->TexOverlapSet(fmt::format("Item\\cloak_M_{:02}.dxt", nPattern)); - else - pCloak->TexOverlapSet(static_cast(nullptr)); + if (m_InfoBase.iKnightsGrade <= 2) + sClanMarkTexPath = CGameProcedure::GetSymbolFilename(1, m_InfoBase.iKnightsID, m_InfoBase.iMarkVersion); // Clan Mark + // TODO: serverIndex is temp hard coded to 1 for testing, + // need to talk to someone smarter than me to confirm if + // this is implemented on the server side yet as I cant find the packet - pCloak->GetCloak()->Init(pCloak); + pCloak->Init(pCloak->Mesh(), SColourTexPath, sClanMarkTexPath, sPatternTexPath); } CN3CPart* CPlayerBase::PartSet(e_PartPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) diff --git a/src/Client/WarFare/PlayerBase.h b/src/Client/WarFare/PlayerBase.h index 59edf13a7..40a4c19b4 100644 --- a/src/Client/WarFare/PlayerBase.h +++ b/src/Client/WarFare/PlayerBase.h @@ -41,12 +41,15 @@ struct __InfoPlayerBase int iHP; int iMP; int iMPMax; - int iAuthority; // 권한 - 0 관리자, 1 - 일반유저, 255 - 블럭당한 유저... - int iKnightsID; // Clan ID + int iAuthority; // 권한 - 0 관리자, 1 - 일반유저, 255 - 블럭당한 유저... + int iKnightsID; // Clan ID int iAllianceID; int iKnightsWarEnemyID; + int iKnightsGrade; // Clan grade + int16_t iMarkVersion; // Clan mark + bool bIsTransformed; - bool bRenderID; // 화면에 ID 를 찍는지.. + bool bRenderID; // 화면에 ID 를 찍는지.. __InfoPlayerBase() { @@ -70,7 +73,10 @@ struct __InfoPlayerBase iKnightsID = 0; iAllianceID = 0; iKnightsWarEnemyID = 0; + iKnightsGrade = 0; + iMarkVersion = 0; bRenderID = true; + bIsTransformed = false; } }; @@ -412,9 +418,10 @@ class CPlayerBase : public CGameBase } virtual CN3CPart* PartSet(e_PartPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt); - virtual CN3CPlugBase* PlugSet(e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt); + virtual CN3CPlugBase* PlugSet( + e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, bool isForce = false); virtual void DurabilitySet(e_ItemSlot eSlot, int iDurability); - void AttachCloak(int16_t sCapeID); + void AttachCloak(int16_t sCapeID, bool isForce = false); void TickYaw(); // 회전값 처리. void TickAnimation(); // 에니메이션 처리. diff --git a/src/Client/WarFare/PlayerMySelf.cpp b/src/Client/WarFare/PlayerMySelf.cpp index 49b5af295..c8b17d261 100644 --- a/src/Client/WarFare/PlayerMySelf.cpp +++ b/src/Client/WarFare/PlayerMySelf.cpp @@ -566,8 +566,16 @@ void CPlayerMySelf::InventoryChrAnimationInitialize() } CN3CPlugBase* CPlayerMySelf::PlugSet( - e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) + e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, bool isForce) { + if (m_InfoBase.bIsTransformed && !isForce) // Skip plug if player is transformed + { + if (pItemBasic) + m_pItemPlugBasics[ePos] = pItemBasic; + if (pItemExt) + m_pItemPlugExts[ePos] = pItemExt; + return nullptr; + } int iJoint = 0; if (PLUG_POS_RIGHTHAND == ePos) { @@ -595,7 +603,7 @@ CN3CPlugBase* CPlayerMySelf::PlugSet( } else if (PLUG_POS_BACK == ePos) { - return CPlayerBase::PlugSet(ePos, szFN, pItemBasic, pItemExt); + return CPlayerBase::PlugSet(ePos, szFN, pItemBasic, pItemExt, isForce); } else { @@ -615,7 +623,7 @@ CN3CPlugBase* CPlayerMySelf::PlugSet( } this->SetSoundPlug(pItemBasic); - return CPlayerBase::PlugSet(ePos, szFN, pItemBasic, pItemExt); + return CPlayerBase::PlugSet(ePos, szFN, pItemBasic, pItemExt, isForce); } CN3CPart* CPlayerMySelf::PartSet(e_PartPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) diff --git a/src/Client/WarFare/PlayerMySelf.h b/src/Client/WarFare/PlayerMySelf.h index 8648a3169..036edd22d 100644 --- a/src/Client/WarFare/PlayerMySelf.h +++ b/src/Client/WarFare/PlayerMySelf.h @@ -78,8 +78,8 @@ class CPlayerMySelf : public CPlayerBase bool InitChr(__TABLE_PLAYER_LOOKS* pTblUPC) override; CN3CPart* PartSet(e_PartPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) override; - CN3CPlugBase* PlugSet( - e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt) override; + CN3CPlugBase* PlugSet(e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, + bool isForce = false) override; void Tick() override; void Render(float fSunAngle) override; diff --git a/src/N3Base/N3Chr.cpp b/src/N3Base/N3Chr.cpp index 45d44e9c7..97d847960 100644 --- a/src/N3Base/N3Chr.cpp +++ b/src/N3Base/N3Chr.cpp @@ -1047,6 +1047,11 @@ CN3CPlug_Cloak::~CN3CPlug_Cloak() void CN3CPlug_Cloak::Release() { +#ifdef _N3GAME + s_MngTex.Delete(&m_pTexColour); + s_MngTex.Delete(&m_pTexClanMark); + s_MngTex.Delete(&m_pTexPattern); +#endif s_MngMesh.Delete(&m_pMesh); CN3CPlugBase::Release(); } @@ -1096,9 +1101,6 @@ bool CN3CPlug_Cloak::Load(File& file) return false; // mesh file missing; CloakPlugSet will clean up if (Tex() == nullptr) TexSet("Item\\cloak_basic.dxt"); -#ifdef _N3GAME - m_Cloak.Init(this); -#endif return true; } @@ -1119,7 +1121,7 @@ void CN3CPlug_Cloak::Render(const __Matrix44& mtxParent, const __Matrix44& mtxJo mtx = m_Matrix; mtx *= mtxJoint; mtx *= mtxParent; - m_Cloak.Render(mtx, TexOverlap()); + m_Cloak.Render(mtx, m_pTexColour, m_pTexPattern, m_pTexClanMark); #endif } @@ -1129,6 +1131,44 @@ void CN3CPlug_Cloak::SetLOD(int nLOD) m_Cloak.SetLOD(nLOD); #endif } + +bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, + const std::string& sClanMarkTex, const std::string& sPatternTex) +{ + //Release(); + + // Release textures only — do NOT delete m_pMesh here, + // pMesh may point to the same mesh we currently hold + #ifdef _N3GAME + s_MngTex.Delete(&m_pTexColour); + s_MngTex.Delete(&m_pTexClanMark); + s_MngTex.Delete(&m_pTexPattern); + #endif + + if (!pMesh) + return false; + + if (!pMesh) + return false; + //if (pMesh->VertexCount() != 49) + // return false; + //if (pMesh->IndexCount() != 216) + // return false; + + m_pMesh = pMesh; + + m_pTexColour = s_MngTex.Get(sColourTex, true, 0); + m_pTexClanMark = s_MngTex.Get(sClanMarkTex, true, 0); + m_pTexPattern = s_MngTex.Get(sPatternTex, true, 0); + //__ASSERT(m_pMesh && m_pTex, "IN CN3Cloak, Mesh or m_pTex is null"); + + m_Cloak.InitMeshTex(pMesh, Tex()); + SetLOD(0); + m_Cloak.InitPhysics(); + + return true; +} + // CN3cPlug_Cloak Codes End here ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// @@ -1938,7 +1978,7 @@ void CN3Chr::Render() //////////////////////////////////////////////////// } - // Clan cape render + // Cloak render if (m_pCloakPlug != nullptr && m_pCloakPlug->m_bVisible && m_pCloakPlug->m_nJointIndex >= 0) { m_pCloakPlug->Render(m_Matrix, m_MtxJoints[m_pCloakPlug->m_nJointIndex]); @@ -2253,16 +2293,16 @@ void CN3Chr::PlugAlloc(int iCount) } } -CN3CPlug_Cloak* CN3Chr::CloakPlugSet(const std::string& szFN) +CN3CPlug_Cloak* CN3Chr::CloakPlugSet(const std::string& sMeshPath) { delete m_pCloakPlug; m_pCloakPlug = nullptr; - if (szFN.empty()) + if (sMeshPath.empty()) return nullptr; m_pCloakPlug = new CN3CPlug_Cloak(); - if (!m_pCloakPlug->LoadFromFile(szFN)) + if (!m_pCloakPlug->LoadFromFile(sMeshPath)) { delete m_pCloakPlug; m_pCloakPlug = nullptr; diff --git a/src/N3Base/N3Chr.h b/src/N3Base/N3Chr.h index 4c7dcaeb5..7bfeea04e 100644 --- a/src/N3Base/N3Chr.h +++ b/src/N3Base/N3Chr.h @@ -289,10 +289,15 @@ class CN3CPlug_Cloak : public CN3CPlugBase { return &m_Cloak; } + bool Init(CN3Mesh* pMesh, const std::string& sColourTex, const std::string& sClanMarkTex, + const std::string& sPatternTex); #endif protected: #ifdef _N3GAME CN3Cloak m_Cloak; + CN3Texture* m_pTexColour = nullptr; + CN3Texture* m_pTexClanMark = nullptr; + CN3Texture* m_pTexPattern = nullptr; #endif CN3Mesh* m_pMesh = nullptr; @@ -341,7 +346,7 @@ class CN3Chr : public CN3TransformCollision std::vector m_Plugs; // 이 캐릭터에 붙이는 무기등의 Data Pointer List std::vector<__VertexColor*> m_vTraces; // Plug Trace Polygon class CN3FXPlug* m_pFXPlug; // 캐릭터에 FX를 붙이는 것. - CN3CPlug_Cloak* m_pCloakPlug = nullptr; // Clan cape attachment + CN3CPlug_Cloak* m_pCloakPlug = nullptr; // Cloak attachment // 조인트의 일부분이 따로 에니메이션 되야 한다면.. 조인트 인덱스 시작 번호 int m_nJointPartStarts[MAX_CHR_ANI_PART]; @@ -480,7 +485,7 @@ class CN3Chr : public CN3TransformCollision return m_Plugs[iIndex]; } - CN3CPlug_Cloak* CloakPlugSet(const std::string& szFN); + CN3CPlug_Cloak* CloakPlugSet(const std::string& sMeshPath); CN3CPlug_Cloak* CloakPlug() { return m_pCloakPlug; diff --git a/src/N3Base/N3Cloak.cpp b/src/N3Base/N3Cloak.cpp index 67674d5f8..eb4f2e1ad 100644 --- a/src/N3Base/N3Cloak.cpp +++ b/src/N3Base/N3Cloak.cpp @@ -35,6 +35,7 @@ CN3Cloak::~CN3Cloak() delete[] m_pParticle; delete[] m_pIndex; delete[] m_pVertex; + delete[] m_pIndexMark; } void CN3Cloak::Release() @@ -47,23 +48,10 @@ void CN3Cloak::Release() delete[] m_pVertex; m_pVertex = nullptr; -} - -void CN3Cloak::Init(CN3CPlug_Cloak* pPlugCloak) -{ - m_pTex = pPlugCloak->Tex(); - m_pMesh = pPlugCloak->Mesh(); - __ASSERT(m_pMesh && m_pTex, "IN CN3Cloak, Mesh or m_pTex is null"); - SetLOD(0); - - // - m_GravityForce.x = 0.0f; - m_GravityForce.z = 0.0005f; - //m_GravityForce.y = -0.0025f; - m_GravityForce.y = -0.0005f; - - m_Force.x = m_Force.y = m_Force.z = 0.0f; + delete[] m_pIndexMark; + m_pIndexMark = nullptr; + m_nIndexCountMark = 0; } void CN3Cloak::Tick(int /*nLOD*/, float fYaw, e_CloakMove eCloakMove) @@ -103,28 +91,26 @@ void CN3Cloak::Tick(int /*nLOD*/, float fYaw, e_CloakMove eCloakMove) m_Force.x = m_Force.y = m_Force.z = 0.0f; } -void CN3Cloak::Render(__Matrix44& mtx, CN3Texture* pTexColour) +void CN3Cloak::Render( + __Matrix44& mtx, CN3Texture* pTexColour, CN3Texture* pTexPattern, CN3Texture* pTexClanMark) { if (m_nLOD < 0 || m_pTex == nullptr) return; s_lpD3DDev->SetTransform(D3DTS_WORLD, mtx.toD3D()); - DWORD dwCull = 0, dwLight = 0; - s_lpD3DDev->GetRenderState(D3DRS_LIGHTING, &dwLight); + DWORD dwCull = 0; s_lpD3DDev->GetRenderState(D3DRS_CULLMODE, &dwCull); - - // - //s_lpD3DDev->SetRenderState( D3DRS_LIGHTING, FALSE); if (dwCull != D3DCULL_NONE) s_lpD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + // Mesh s_lpD3DDev->SetTexture(0, m_pTex->Get()); - s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + // Colour if (pTexColour != nullptr) { s_lpD3DDev->SetTexture(1, pTexColour->Get()); @@ -132,7 +118,6 @@ void CN3Cloak::Render(__Matrix44& mtx, CN3Texture* pTexColour) s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); - s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); } else { @@ -140,12 +125,56 @@ void CN3Cloak::Render(__Matrix44& mtx, CN3Texture* pTexColour) s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); } + // Pattern + if (pTexPattern != nullptr) + { + s_lpD3DDev->SetTexture(2, pTexPattern->Get()); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, 0); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT); + s_lpD3DDev->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE); + } + else + { + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); + } + s_lpD3DDev->SetFVF(FVF_VNT1); s_lpD3DDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, m_nVertexCount, m_nIndexCount / 3, m_pIndex, D3DFMT_INDEX16, m_pVertex, sizeof(__VertexT1)); + if (pTexClanMark != nullptr && m_pIndexMark != nullptr && m_nIndexCountMark > 0) + { + const float fDepthBias = -0.001f; + s_lpD3DDev->SetRenderState(D3DRS_DEPTHBIAS, *reinterpret_cast(&fDepthBias)); + + s_lpD3DDev->SetTexture(0, pTexClanMark->Get()); + s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); + + D3DMATRIX texMat = { 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 1.0f }; + s_lpD3DDev->SetTransform(D3DTS_TEXTURE0, &texMat); + s_lpD3DDev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2); + + s_lpD3DDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, m_nVertexCount, m_nIndexCountMark, + m_pIndexMark, D3DFMT_INDEX16, m_pVertex, sizeof(__VertexT1)); + + const float fZero = 0.0f; + s_lpD3DDev->SetRenderState(D3DRS_DEPTHBIAS, *reinterpret_cast(&fZero)); + + s_lpD3DDev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); - /* Grid for debugging the clan cape movement + // Restore stage 0 + s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + s_lpD3DDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + } + + /* Grid for debugging the cloake movement __VertexXyzColor Vtx[2] {}; __VertexT1 *pTemp = m_pVertex; Vtx[0].Set(pTemp->x, pTemp->y, pTemp->z, 0xffffffff); @@ -193,7 +222,9 @@ void CN3Cloak::Render(__Matrix44& mtx, CN3Texture* pTexColour) // restore renderstate. s_lpD3DDev->SetTexture(1, nullptr); + s_lpD3DDev->SetTexture(2, nullptr); s_lpD3DDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + s_lpD3DDev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); if (dwCull != D3DCULL_NONE) s_lpD3DDev->SetRenderState(D3DRS_CULLMODE, dwCull); } @@ -358,7 +389,35 @@ void CN3Cloak::SetLOD(int nLevel) m_nIndexCount = nIndexCount; // } - break; + // Center-quad index list for clan mark + { + constexpr int MARK_COL_MIN = 2, MARK_COL_MAX = 4; // exclusive + constexpr int MARK_ROW_MIN = 0, MARK_ROW_MAX = 2; // exclusive + int nMarkQuads = (MARK_COL_MAX - MARK_COL_MIN) * (MARK_ROW_MAX - MARK_ROW_MIN); + delete[] m_pIndexMark; + m_pIndexMark = new uint16_t[nMarkQuads * 6]; + m_nIndexCountMark = 0; + uint16_t* pDst = m_pIndexMark; + for (int row = MARK_ROW_MIN; row < MARK_ROW_MAX; row++) + { + for (int col = MARK_COL_MIN; col < MARK_COL_MAX; col++) + { + uint16_t v0 = (row + CLOAK_SKIP_LINE) * m_nGridW + col; + uint16_t v1 = (row + CLOAK_SKIP_LINE) * m_nGridW + col + 1; + uint16_t v2 = (row + CLOAK_SKIP_LINE + 1) * m_nGridW + col; + uint16_t v3 = (row + CLOAK_SKIP_LINE + 1) * m_nGridW + col + 1; + pDst[0] = v0; + pDst[1] = v1; + pDst[2] = v3; + pDst[3] = v3; + pDst[4] = v2; + pDst[5] = v0; + pDst += 6; + m_nIndexCountMark += 2; + } + } + } + break; default: break; diff --git a/src/N3Base/N3Cloak.h b/src/N3Base/N3Cloak.h index a0359e926..517cc2074 100644 --- a/src/N3Base/N3Cloak.h +++ b/src/N3Base/N3Cloak.h @@ -71,10 +71,20 @@ class CN3Cloak : public CN3Base vx = vx1, vy = vy1, vz = vz1; } }; - - void Init(CN3CPlug_Cloak* pPlugCloak); void SetLOD(int nLevel); void ApplyOffset(__Vector3& vDif); + void InitMeshTex(CN3Mesh* pMesh, CN3Texture* pTex) + { + m_pMesh = pMesh; + m_pTex = pTex; + } + void InitPhysics() + { + m_GravityForce = { 0.0f, -0.0015f, 0.0f }; + m_Force = {}; + m_fAnchorPreserveTime = 0.0f; + m_fOffsetRecoveryTime = 0.0f; + } protected: // Anchor @@ -107,9 +117,13 @@ class CN3Cloak : public CN3Base void TickYaw(float fYaw); void TickByPlayerMotion(e_CloakMove eCurMove); + uint16_t* m_pIndexMark = nullptr; + int m_nIndexCountMark = 0; + public: virtual void Tick(int nLOD, float fYaw, e_CloakMove eCurMove); - virtual void Render(__Matrix44& mtx, CN3Texture* pTexColour = nullptr); + virtual void Render(__Matrix44& mtx, CN3Texture* pTexColour = nullptr, + CN3Texture* pTexPattern = nullptr, CN3Texture* pTexClanMark = nullptr); void Release() override; }; From a002b2b7b145b8612851e1dcabb799accba84481 Mon Sep 17 00:00:00 2001 From: reignsprime Date: Sun, 12 Apr 2026 21:46:34 +1000 Subject: [PATCH 3/7] King cloak and some cleanup --- src/Client/WarFare/GameDef.h | 1 + src/Client/WarFare/GameProcMain.cpp | 13 ++++++----- src/Client/WarFare/PlayerBase.cpp | 34 +++++++++++++++++++---------- src/Client/WarFare/PlayerBase.h | 2 +- src/N3Base/N3Chr.cpp | 26 ++++++++++------------ src/N3Base/N3Chr.h | 8 +++---- 6 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/Client/WarFare/GameDef.h b/src/Client/WarFare/GameDef.h index 5c4018840..b317b11c6 100644 --- a/src/Client/WarFare/GameDef.h +++ b/src/Client/WarFare/GameDef.h @@ -443,6 +443,7 @@ struct __InfoPlayerOther iCity = 0; iKnightsGrade = 0; iKnightsRank = 0; + iRank = 0; iTitle = 0; szKnights.clear(); diff --git a/src/Client/WarFare/GameProcMain.cpp b/src/Client/WarFare/GameProcMain.cpp index 3c02b4d2e..44158ffd1 100644 --- a/src/Client/WarFare/GameProcMain.cpp +++ b/src/Client/WarFare/GameProcMain.cpp @@ -2176,10 +2176,10 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) // 기본값 읽기.. //////////////////////////////////////////////////////////// - InitPlayerPosition(__Vector3(fX, fY, fZ)); // 플레이어 위치 초기화.. 일으켜 세우고, 기본동작을 취하게 한다. + InitPlayerPosition(__Vector3(fX, fY, fZ)); // 플레이어 위치 초기화.. 일으켜 세우고, 기본동작을 취하게 한다. s_pPlayer->RegenerateCollisionMesh(); - s_pPlayer->AttachCloak(sCapeID); // Cloak - s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. + s_pPlayer->AttachCloak(sCapeID, s_pPlayer->m_InfoExt.iRank); // Cloak + s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. if (m_pUICmdList != nullptr) m_pUICmdList->CreateCategoryList(); @@ -2542,7 +2542,7 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) int iKnightsRank = pkt.read(); // 순위 int16_t sMarkVersion = pkt.read(); // Clan mark - int16_t sCapeID = pkt.read(); // Clan cloak + int16_t sCapeID = pkt.read(); // Cloak int iLevel = pkt.read(); // 레벨... e_Race eRace = (e_Race) pkt.read(); @@ -2574,7 +2574,7 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) /*uint8_t bInvisibilityType =*/pkt.read(); /*int16_t sDirection =*/pkt.read(); /*bool bIsChicken =*/pkt.read(); - /*uint8_t bRank =*/pkt.read(); + uint8_t byRank = pkt.read(); // Noble rank?? /*uint8_t bKnightsRank =*/pkt.read(); /*uint8_t bPersonalRank =*/pkt.read(); @@ -2624,8 +2624,9 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) pUPC->m_InfoBase.iAuthority = byAuthority; pUPC->Init(eRace, iFace, iHair, dwItemIDs, iItemDurabilities, byItemFlags); pUPC->RotateTo(DegreesToRadians(rand() % 360), true); + pUPC->m_InfoBase.iMarkVersion = sMarkVersion; pUPC->KnightsInfoSet(iKnightsID, szKnightsName, iKnightsGrade, iKnightsRank); - pUPC->AttachCloak(sCapeID); + pUPC->AttachCloak(sCapeID, byRank); //__KnightsInfoBase* pKIB = m_pUIKnightsOp->KnightsInfoFind(iKightsID); //if(pKIB) pUPC->KnightsNameSet(pKIB->szName, 0xffff0000); diff --git a/src/Client/WarFare/PlayerBase.cpp b/src/Client/WarFare/PlayerBase.cpp index b737b1f97..2107c8625 100644 --- a/src/Client/WarFare/PlayerBase.cpp +++ b/src/Client/WarFare/PlayerBase.cpp @@ -1870,7 +1870,7 @@ CN3CPlugBase* CPlayerBase::PlugSet( return pPlug; } -void CPlayerBase::AttachCloak(int16_t sCapeID, bool isForce) +void CPlayerBase::AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce) { PlugSet(PLUG_POS_BACK, "", nullptr, nullptr, isForce); @@ -1883,20 +1883,30 @@ void CPlayerBase::AttachCloak(int16_t sCapeID, bool isForce) if (pCloak == nullptr) return; - // TODO: Implement king cloak, need to look at binary client some more + std::string SColourTexPath; + std::string sPatternTexPath; + std::string sClanMarkTexPath; - int nColour = sCapeID % 100; - int nPattern = sCapeID / 100; + if (iNobleRank == 1) + { + SColourTexPath = "Item\\Cloak_C_99.dxt"; // King cloak + sPatternTexPath = ""; + } + else + { + int nColour = sCapeID % 100; + int nPattern = sCapeID / 100; - std::string SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour - std::string sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern - std::string sClanMarkTexPath = ""; // No Clan Mark + SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour + sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern + sClanMarkTexPath = ""; // No Clan Mark - if (m_InfoBase.iKnightsGrade <= 2) - sClanMarkTexPath = CGameProcedure::GetSymbolFilename(1, m_InfoBase.iKnightsID, m_InfoBase.iMarkVersion); // Clan Mark - // TODO: serverIndex is temp hard coded to 1 for testing, - // need to talk to someone smarter than me to confirm if - // this is implemented on the server side yet as I cant find the packet + if (m_InfoBase.iKnightsGrade <= 2) + sClanMarkTexPath = CGameProcedure::GetSymbolFilename(1, m_InfoBase.iKnightsID, m_InfoBase.iMarkVersion); // Clan Mark + // TODO: serverIndex is temp hard coded to 1 for testing, + // need to talk to someone smarter than me to confirm if + // this is implemented on the server side yet as I cant find the packet + } pCloak->Init(pCloak->Mesh(), SColourTexPath, sClanMarkTexPath, sPatternTexPath); } diff --git a/src/Client/WarFare/PlayerBase.h b/src/Client/WarFare/PlayerBase.h index 40a4c19b4..0550828d5 100644 --- a/src/Client/WarFare/PlayerBase.h +++ b/src/Client/WarFare/PlayerBase.h @@ -421,7 +421,7 @@ class CPlayerBase : public CGameBase virtual CN3CPlugBase* PlugSet( e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, bool isForce = false); virtual void DurabilitySet(e_ItemSlot eSlot, int iDurability); - void AttachCloak(int16_t sCapeID, bool isForce = false); + void AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce = false); void TickYaw(); // 회전값 처리. void TickAnimation(); // 에니메이션 처리. diff --git a/src/N3Base/N3Chr.cpp b/src/N3Base/N3Chr.cpp index 97d847960..a3e7fbb54 100644 --- a/src/N3Base/N3Chr.cpp +++ b/src/N3Base/N3Chr.cpp @@ -1098,9 +1098,7 @@ bool CN3CPlug_Cloak::Load(File& file) ReCalcMatrix(); if (m_pMesh == nullptr) - return false; // mesh file missing; CloakPlugSet will clean up - if (Tex() == nullptr) - TexSet("Item\\cloak_basic.dxt"); + return false; // mesh file missing. CloakPlugSet will clean up return true; } @@ -1132,24 +1130,20 @@ void CN3CPlug_Cloak::SetLOD(int nLOD) #endif } -bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, - const std::string& sClanMarkTex, const std::string& sPatternTex) +bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, const std::string& sClanMarkTex, const std::string& sPatternTex) { //Release(); // Release textures only — do NOT delete m_pMesh here, // pMesh may point to the same mesh we currently hold - #ifdef _N3GAME - s_MngTex.Delete(&m_pTexColour); - s_MngTex.Delete(&m_pTexClanMark); - s_MngTex.Delete(&m_pTexPattern); - #endif - if (!pMesh) - return false; + s_MngTex.Delete(&m_pTexColour); + s_MngTex.Delete(&m_pTexClanMark); + s_MngTex.Delete(&m_pTexPattern); if (!pMesh) return false; + //if (pMesh->VertexCount() != 49) // return false; //if (pMesh->IndexCount() != 216) @@ -1162,9 +1156,13 @@ bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, m_pTexPattern = s_MngTex.Get(sPatternTex, true, 0); //__ASSERT(m_pMesh && m_pTex, "IN CN3Cloak, Mesh or m_pTex is null"); - m_Cloak.InitMeshTex(pMesh, Tex()); + TexSet(sColourTex); + #ifdef _N3GAME + m_Cloak.InitMeshTex(pMesh, Tex()); + m_Cloak.InitPhysics(); + #endif SetLOD(0); - m_Cloak.InitPhysics(); + return true; } diff --git a/src/N3Base/N3Chr.h b/src/N3Base/N3Chr.h index 7bfeea04e..c665f4a08 100644 --- a/src/N3Base/N3Chr.h +++ b/src/N3Base/N3Chr.h @@ -284,22 +284,22 @@ class CN3CPlug_Cloak : public CN3CPlugBase void Release() override; void SetLOD(int nLOD); + bool Init(CN3Mesh* pMesh, const std::string& sColourTex, const std::string& sClanMarkTex, + const std::string& sPatternTex); #ifdef _N3GAME CN3Cloak* GetCloak() { return &m_Cloak; } - bool Init(CN3Mesh* pMesh, const std::string& sColourTex, const std::string& sClanMarkTex, - const std::string& sPatternTex); #endif protected: #ifdef _N3GAME CN3Cloak m_Cloak; +#endif + CN3Mesh* m_pMesh = nullptr; CN3Texture* m_pTexColour = nullptr; CN3Texture* m_pTexClanMark = nullptr; CN3Texture* m_pTexPattern = nullptr; -#endif - CN3Mesh* m_pMesh = nullptr; public: CN3Mesh* Mesh() From b850bb80b98744d8ed4ca9ae43befe09bda19afc Mon Sep 17 00:00:00 2001 From: reignsprime Date: Sun, 12 Apr 2026 21:54:26 +1000 Subject: [PATCH 4/7] clang cleanup --- src/Client/WarFare/PlayerBase.cpp | 19 +++++++++---------- src/N3Base/N3Chr.cpp | 14 +++++++------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Client/WarFare/PlayerBase.cpp b/src/Client/WarFare/PlayerBase.cpp index 2107c8625..0fec2b900 100644 --- a/src/Client/WarFare/PlayerBase.cpp +++ b/src/Client/WarFare/PlayerBase.cpp @@ -1883,29 +1883,28 @@ void CPlayerBase::AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce) if (pCloak == nullptr) return; - std::string SColourTexPath; + std::string SColourTexPath; std::string sPatternTexPath; std::string sClanMarkTexPath; if (iNobleRank == 1) { SColourTexPath = "Item\\Cloak_C_99.dxt"; // King cloak - sPatternTexPath = ""; } else { - int nColour = sCapeID % 100; - int nPattern = sCapeID / 100; + int nColour = sCapeID % 100; + int nPattern = sCapeID / 100; - SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour - sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern - sClanMarkTexPath = ""; // No Clan Mark + SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour + sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern + sClanMarkTexPath = ""; // No Clan Mark if (m_InfoBase.iKnightsGrade <= 2) sClanMarkTexPath = CGameProcedure::GetSymbolFilename(1, m_InfoBase.iKnightsID, m_InfoBase.iMarkVersion); // Clan Mark - // TODO: serverIndex is temp hard coded to 1 for testing, - // need to talk to someone smarter than me to confirm if - // this is implemented on the server side yet as I cant find the packet + // TODO: serverIndex is temp hard coded to 1 for testing, + // need to talk to someone smarter than me to confirm if + // this is implemented on the server side yet as I cant find the packet } pCloak->Init(pCloak->Mesh(), SColourTexPath, sClanMarkTexPath, sPatternTexPath); diff --git a/src/N3Base/N3Chr.cpp b/src/N3Base/N3Chr.cpp index a3e7fbb54..dbbbbd408 100644 --- a/src/N3Base/N3Chr.cpp +++ b/src/N3Base/N3Chr.cpp @@ -1130,11 +1130,12 @@ void CN3CPlug_Cloak::SetLOD(int nLOD) #endif } -bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, const std::string& sClanMarkTex, const std::string& sPatternTex) +bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, + const std::string& sClanMarkTex, const std::string& sPatternTex) { //Release(); - // Release textures only — do NOT delete m_pMesh here, + // Release textures only — do NOT delete m_pMesh here, // pMesh may point to the same mesh we currently hold s_MngTex.Delete(&m_pTexColour); @@ -1157,13 +1158,12 @@ bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, const s //__ASSERT(m_pMesh && m_pTex, "IN CN3Cloak, Mesh or m_pTex is null"); TexSet(sColourTex); - #ifdef _N3GAME - m_Cloak.InitMeshTex(pMesh, Tex()); - m_Cloak.InitPhysics(); - #endif +#ifdef _N3GAME + m_Cloak.InitMeshTex(pMesh, Tex()); + m_Cloak.InitPhysics(); +#endif SetLOD(0); - return true; } From 69944a6405fa2ebcd2fee4f083f422895c6a6c50 Mon Sep 17 00:00:00 2001 From: reignsprime Date: Thu, 30 Apr 2026 14:09:36 +1000 Subject: [PATCH 5/7] Changes to gravity, moved iRank to InfoBase, setup serverindex packet reads --- src/Client/WarFare/GameDef.h | 2 - src/Client/WarFare/GameProcMain.cpp | 24 ++++++-- src/Client/WarFare/GameProcMain.h | 2 + src/Client/WarFare/GameProcedure.cpp | 5 ++ src/Client/WarFare/GameProcedure.h | 1 + src/Client/WarFare/PlayerBase.cpp | 30 +++++----- src/Client/WarFare/PlayerBase.h | 4 +- src/Client/WarFare/PlayerMySelf.cpp | 8 --- src/Client/WarFare/UIImageTooltipDlg.cpp | 2 +- src/Client/WarFare/UIInventory.cpp | 2 +- src/N3Base/N3Cloak.cpp | 70 ++++++++++++++++-------- 11 files changed, 95 insertions(+), 55 deletions(-) diff --git a/src/Client/WarFare/GameDef.h b/src/Client/WarFare/GameDef.h index b317b11c6..cffbb3cc9 100644 --- a/src/Client/WarFare/GameDef.h +++ b/src/Client/WarFare/GameDef.h @@ -427,7 +427,6 @@ struct __InfoPlayerOther int iKnightsGrade; // Clan grade int iKnightsRank; // Clan ranking - int iRank; // Noble rank - used to identify high-ranking titles like King [1], Senator [2]. int iTitle; // Bitmask representing various titles/roles including: // Clan Leader, Clan Assistant, Castle Lord, Feudal Lord, King, Emperor, Party leader, Solo player @@ -443,7 +442,6 @@ struct __InfoPlayerOther iCity = 0; iKnightsGrade = 0; iKnightsRank = 0; - iRank = 0; iTitle = 0; szKnights.clear(); diff --git a/src/Client/WarFare/GameProcMain.cpp b/src/Client/WarFare/GameProcMain.cpp index 44158ffd1..9779cc2a7 100644 --- a/src/Client/WarFare/GameProcMain.cpp +++ b/src/Client/WarFare/GameProcMain.cpp @@ -1088,6 +1088,9 @@ bool CGameProcMain::ProcessPacket(Packet& pkt) } } break; + case WIZ_SERVER_INDEX: + this->MsgRecv_ServerIndex(pkt); + return true; case WIZ_ITEM_UPGRADE: MsgRecv_ItemUpgrade(pkt); @@ -1867,7 +1870,7 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) __ASSERT(pLooks, "failed find character resource data"); s_pPlayer->InitChr(pLooks); // 관절 세팅.. - s_pPlayer->m_InfoExt.iRank = pkt.read(); + s_pPlayer->m_InfoBase.iRank = pkt.read(); s_pPlayer->m_InfoExt.iTitle = pkt.read(); s_pPlayer->m_InfoBase.iLevel = pkt.read(); s_pPlayer->m_InfoExt.iLevelPrev = s_pPlayer->m_InfoBase.iLevel; @@ -2176,10 +2179,10 @@ bool CGameProcMain::MsgRecv_MyInfo_All(Packet& pkt) // 기본값 읽기.. //////////////////////////////////////////////////////////// - InitPlayerPosition(__Vector3(fX, fY, fZ)); // 플레이어 위치 초기화.. 일으켜 세우고, 기본동작을 취하게 한다. + InitPlayerPosition(__Vector3(fX, fY, fZ)); // 플레이어 위치 초기화.. 일으켜 세우고, 기본동작을 취하게 한다. s_pPlayer->RegenerateCollisionMesh(); - s_pPlayer->AttachCloak(sCapeID, s_pPlayer->m_InfoExt.iRank); // Cloak - s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. + s_pPlayer->AttachCloak(sCapeID); // Cloak + s_pOPMgr->Release(); // 다른 유저 관리 클래스 초기화.. if (m_pUICmdList != nullptr) m_pUICmdList->CreateCategoryList(); @@ -2574,7 +2577,7 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) /*uint8_t bInvisibilityType =*/pkt.read(); /*int16_t sDirection =*/pkt.read(); /*bool bIsChicken =*/pkt.read(); - uint8_t byRank = pkt.read(); // Noble rank?? + uint8_t byRank = pkt.read(); // Noble rank - used to identify high-ranking titles like King [1], Senator [2] /*uint8_t bKnightsRank =*/pkt.read(); /*uint8_t bPersonalRank =*/pkt.read(); @@ -2622,11 +2625,12 @@ bool CGameProcMain::MsgRecv_UserIn(Packet& pkt, bool bWithFX) pUPC->m_InfoBase.eClass = eClass; pUPC->m_InfoBase.iLevel = iLevel; pUPC->m_InfoBase.iAuthority = byAuthority; + pUPC->m_InfoBase.iRank = byRank; pUPC->Init(eRace, iFace, iHair, dwItemIDs, iItemDurabilities, byItemFlags); pUPC->RotateTo(DegreesToRadians(rand() % 360), true); pUPC->m_InfoBase.iMarkVersion = sMarkVersion; pUPC->KnightsInfoSet(iKnightsID, szKnightsName, iKnightsGrade, iKnightsRank); - pUPC->AttachCloak(sCapeID, byRank); + pUPC->AttachCloak(sCapeID); //__KnightsInfoBase* pKIB = m_pUIKnightsOp->KnightsInfoFind(iKightsID); //if(pKIB) pUPC->KnightsNameSet(pKIB->szName, 0xffff0000); @@ -8046,3 +8050,11 @@ void CGameProcMain::MsgRecv_ZoneAbility(Packet& pkt) s_pPlayer->m_InfoExt.sZoneTariff = pkt.read(); } } + +void CGameProcMain::MsgRecv_ServerIndex(Packet& pkt) +{ + int16_t sResult = pkt.read(); + if (sResult == 0) // 1 = success, 0 = server index unavailable + return; + m_nServerIndex = pkt.read(); +} diff --git a/src/Client/WarFare/GameProcMain.h b/src/Client/WarFare/GameProcMain.h index 7d566f06c..c1aba6adf 100644 --- a/src/Client/WarFare/GameProcMain.h +++ b/src/Client/WarFare/GameProcMain.h @@ -108,6 +108,8 @@ class CGameProcMain : public CGameProcedure bool MsgRecv_CharacterSelect(Packet& pkt) override; // virtual int MsgRecv_VersionCheck(Packet& pkt) override; // virtual + void MsgRecv_ServerIndex(Packet& pkt); // Server index + int m_nServerIndex = 0; bool MsgRecv_MyInfo_All(Packet& pkt); void MsgRecv_MyInfo_HP(Packet& pkt); diff --git a/src/Client/WarFare/GameProcedure.cpp b/src/Client/WarFare/GameProcedure.cpp index 1f954122c..d17877038 100644 --- a/src/Client/WarFare/GameProcedure.cpp +++ b/src/Client/WarFare/GameProcedure.cpp @@ -716,6 +716,11 @@ std::string CGameProcedure::GetSymbolFilename(const int serverIndex, const int k return fmt::format("symbol_us\\s_{}_{}_{}.dxt", serverIndex, knightsId, markVersion); } +int CGameProcedure::GetServerIndex() +{ + return s_pProcMain ? s_pProcMain->m_nServerIndex : 0; +} + bool CGameProcedure::ProcessPacket(Packet& pkt) { int iCmd = pkt.read(); // 커멘드 파싱.. diff --git a/src/Client/WarFare/GameProcedure.h b/src/Client/WarFare/GameProcedure.h index 44d01e32f..08e45df31 100644 --- a/src/Client/WarFare/GameProcedure.h +++ b/src/Client/WarFare/GameProcedure.h @@ -150,6 +150,7 @@ class CGameProcedure : public CGameBase static void RestoreGameCursor(); static std::string GetSymbolFilename(const int serverIndex, const int knightsId, const int markVersion); + static int GetServerIndex(); protected: virtual bool ProcessPacket(Packet& pkt); diff --git a/src/Client/WarFare/PlayerBase.cpp b/src/Client/WarFare/PlayerBase.cpp index 0fec2b900..b395fe274 100644 --- a/src/Client/WarFare/PlayerBase.cpp +++ b/src/Client/WarFare/PlayerBase.cpp @@ -1723,7 +1723,14 @@ CN3CPlugBase* CPlayerBase::PlugSet( __ASSERT(0, "Invalid Plug Position"); return nullptr; } - + if (m_InfoBase.bIsTransformed && !isForce) // Skip plug if player is transformed + { + if (pItemBasic) + m_pItemPlugBasics[ePos] = pItemBasic; + if (pItemExt) + m_pItemPlugExts[ePos] = pItemExt; + return nullptr; + } int iJoint = 0; if (PLUG_POS_RIGHTHAND == ePos) { @@ -1870,12 +1877,11 @@ CN3CPlugBase* CPlayerBase::PlugSet( return pPlug; } -void CPlayerBase::AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce) +void CPlayerBase::AttachCloak(int16_t sCapeID, bool isForce) { - PlugSet(PLUG_POS_BACK, "", nullptr, nullptr, isForce); + PlugSet(PLUG_POS_BACK, "", nullptr, nullptr, /*isForce=*/true); // Remove cloak - //Temporary to prevent cloaks on all chars while still a work in progress -1 = no cloak - if (sCapeID < 0) + if (sCapeID < 0) // No cloak return; const std::string sMeshPath = fmt::format("Item\\Cloak_{:03}.n3cplug", static_cast(m_InfoBase.eRace)); // Cloak mesh for each race @@ -1887,7 +1893,7 @@ void CPlayerBase::AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce) std::string sPatternTexPath; std::string sClanMarkTexPath; - if (iNobleRank == 1) + if (m_InfoBase.iRank == 1) { SColourTexPath = "Item\\Cloak_C_99.dxt"; // King cloak } @@ -1896,15 +1902,13 @@ void CPlayerBase::AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce) int nColour = sCapeID % 100; int nPattern = sCapeID / 100; - SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour - sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern - sClanMarkTexPath = ""; // No Clan Mark + SColourTexPath = fmt::format("Item\\Cloak_C_{:02}.dxt", nColour); // Cloak colour + sPatternTexPath = fmt::format("Item\\Cloak_M_{:02}.dxt", nPattern); // Cloak pattern + sClanMarkTexPath = ""; // No Clan Mark if (m_InfoBase.iKnightsGrade <= 2) - sClanMarkTexPath = CGameProcedure::GetSymbolFilename(1, m_InfoBase.iKnightsID, m_InfoBase.iMarkVersion); // Clan Mark - // TODO: serverIndex is temp hard coded to 1 for testing, - // need to talk to someone smarter than me to confirm if - // this is implemented on the server side yet as I cant find the packet + sClanMarkTexPath = CGameProcedure::GetSymbolFilename( + CGameProcedure::GetServerIndex(), m_InfoBase.iKnightsID, m_InfoBase.iMarkVersion); // Clan Mark } pCloak->Init(pCloak->Mesh(), SColourTexPath, sClanMarkTexPath, sPatternTexPath); diff --git a/src/Client/WarFare/PlayerBase.h b/src/Client/WarFare/PlayerBase.h index 0550828d5..4d9109ece 100644 --- a/src/Client/WarFare/PlayerBase.h +++ b/src/Client/WarFare/PlayerBase.h @@ -47,6 +47,7 @@ struct __InfoPlayerBase int iKnightsWarEnemyID; int iKnightsGrade; // Clan grade int16_t iMarkVersion; // Clan mark + int iRank; // Noble rank - used to identify high-ranking titles like King [1], Senator [2]. Always 0 for NPCs. bool bIsTransformed; bool bRenderID; // 화면에 ID 를 찍는지.. @@ -75,6 +76,7 @@ struct __InfoPlayerBase iKnightsWarEnemyID = 0; iKnightsGrade = 0; iMarkVersion = 0; + iRank = 0; bRenderID = true; bIsTransformed = false; } @@ -421,7 +423,7 @@ class CPlayerBase : public CGameBase virtual CN3CPlugBase* PlugSet( e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, bool isForce = false); virtual void DurabilitySet(e_ItemSlot eSlot, int iDurability); - void AttachCloak(int16_t sCapeID, int iNobleRank, bool isForce = false); + void AttachCloak(int16_t sCapeID, bool isForce = false); void TickYaw(); // 회전값 처리. void TickAnimation(); // 에니메이션 처리. diff --git a/src/Client/WarFare/PlayerMySelf.cpp b/src/Client/WarFare/PlayerMySelf.cpp index c8b17d261..7722e7416 100644 --- a/src/Client/WarFare/PlayerMySelf.cpp +++ b/src/Client/WarFare/PlayerMySelf.cpp @@ -568,14 +568,6 @@ void CPlayerMySelf::InventoryChrAnimationInitialize() CN3CPlugBase* CPlayerMySelf::PlugSet( e_PlugPosition ePos, const std::string& szFN, __TABLE_ITEM_BASIC* pItemBasic, __TABLE_ITEM_EXT* pItemExt, bool isForce) { - if (m_InfoBase.bIsTransformed && !isForce) // Skip plug if player is transformed - { - if (pItemBasic) - m_pItemPlugBasics[ePos] = pItemBasic; - if (pItemExt) - m_pItemPlugExts[ePos] = pItemExt; - return nullptr; - } int iJoint = 0; if (PLUG_POS_RIGHTHAND == ePos) { diff --git a/src/Client/WarFare/UIImageTooltipDlg.cpp b/src/Client/WarFare/UIImageTooltipDlg.cpp index 93aae1df1..8e99b7dd1 100644 --- a/src/Client/WarFare/UIImageTooltipDlg.cpp +++ b/src/Client/WarFare/UIImageTooltipDlg.cpp @@ -885,7 +885,7 @@ void CUIImageTooltipDlg::CalcTooltipStringNumAndWriteImpl(__IconItemSkill* spIte m_pStr[iIndex]->SetStyle(UI_STR_TYPE_HALIGN, UISTYLE_STRING_ALIGNLEFT); - if (SetTooltipTextColor(pInfoExt->iRank, spItem->pItemBasic->byNeedRank + spItem->pItemExt->siNeedRank)) + if (SetTooltipTextColor(CGameBase::s_pPlayer->m_InfoBase.iRank, spItem->pItemBasic->byNeedRank + spItem->pItemExt->siNeedRank)) m_pStr[iIndex]->SetColor(m_CWhite); else m_pStr[iIndex]->SetColor(m_CRed); diff --git a/src/Client/WarFare/UIInventory.cpp b/src/Client/WarFare/UIInventory.cpp index 71cb13724..2738dafc1 100644 --- a/src/Client/WarFare/UIInventory.cpp +++ b/src/Client/WarFare/UIInventory.cpp @@ -1905,7 +1905,7 @@ bool CUIInventory::IsValidRaceAndClass(__TABLE_ITEM_BASIC* pItem, __TABLE_ITEM_E return false; } - if (pInfoExt->iRank < pItem->byNeedRank + pItemExt->siNeedRank) + if (CGameBase::s_pPlayer->m_InfoBase.iRank < pItem->byNeedRank + pItemExt->siNeedRank) { szMsg = fmt::format_text_resource(IDS_MSG_VALID_CLASSNRACE_LOW_RANK); CGameProcedure::s_pProcMain->MsgOutput(szMsg, 0xffff3b3b); diff --git a/src/N3Base/N3Cloak.cpp b/src/N3Base/N3Cloak.cpp index eb4f2e1ad..e463527d5 100644 --- a/src/N3Base/N3Cloak.cpp +++ b/src/N3Base/N3Cloak.cpp @@ -73,18 +73,20 @@ void CN3Cloak::Tick(int /*nLOD*/, float fYaw, e_CloakMove eCloakMove) // fForceDelay = 0.0f; // fForceDelayLimit = 2.0f + rand()%10; // } - m_fAnchorPreserveTime -= s_fSecPerFrm; - if (m_eAnchorPattern != AMP_NONE) - { - if (m_fAnchorPreserveTime < 0.0f) - { - RestoreAnchorLine(); - } - } + // m_fAnchorPreserveTime -= s_fSecPerFrm; + // if (m_eAnchorPattern != AMP_NONE) + // { + // if (m_fAnchorPreserveTime < 0.0f) + // { + // RestoreAnchorLine(); + // } + // } + // const e_Cloak_AnchorMovePattern prevAnchor = m_eAnchorPattern; TickByPlayerMotion(eCloakMove); TickYaw(fYaw); - + // if (m_eAnchorPattern == prevAnchor && m_eAnchorPattern == AMP_NONE) + // MoveAnchorLine(AMP_NONE, 2.0f); UpdateLocalForce(); ApplyForce(); @@ -481,24 +483,30 @@ void CN3Cloak::ApplyOffset(__Vector3& vDif) void CN3Cloak::TickYaw(float fYaw) { // 회전이 있었다. - if (fYaw != m_fPrevYaw) + /* if (fYaw != m_fPrevYaw) { - if (fYaw - m_fPrevYaw > 0.0f) + const float deltafYaw = fYaw - m_fPrevYaw; + + if (deltafYaw > 2.0f) { if (m_eAnchorPattern == AMP_NONE && m_fAnchorPreserveTime < 0.0f) MoveAnchorLine(AMP_YAWCCW, 2.0f); } - else + else if (deltafYaw < -2.0f) { if (m_eAnchorPattern == AMP_NONE && m_fAnchorPreserveTime < 0.0f) MoveAnchorLine(AMP_YAWCW, 2.0f); } - } + }*/ m_fPrevYaw = fYaw; } void CN3Cloak::TickByPlayerMotion(e_CloakMove eCurMove) { + float angleFactor = 0.0f; + float angleMag = 0.0f; + bool doWind = false; + switch (eCurMove) { case CLOAK_MOVE_STOP: @@ -506,19 +514,27 @@ void CN3Cloak::TickByPlayerMotion(e_CloakMove eCurMove) break; case CLOAK_MOVE_WALK: - m_Force.z = 0.0005f; - m_GravityForce.y = -0.0025f; - if (m_eAnchorPattern == AMP_NONE && m_fAnchorPreserveTime < 0.0f) - MoveAnchorLine(AMP_MOVEXZ, 2.0f); + // m_Force.z = 0.0005f; + m_GravityForce.y = -0.0015f; + angleFactor = 0.314159f; + angleMag = -0.0003f; + doWind = true; + // if (m_eAnchorPattern == AMP_NONE + // && m_fAnchorPreserveTime < 0.0f) + // MoveAnchorLine(AMP_MOVEXZ, 2.0f); //m_GravityForce.y = (rand()%2+1)*-0.0015f; //TRACE("Apply force {}", m_Force.z); break; case CLOAK_MOVE_RUN: - m_Force.z = 0.0009f; - m_GravityForce.y = -0.0025f; - if (m_eAnchorPattern == AMP_NONE && m_fAnchorPreserveTime < 0.0f) - MoveAnchorLine(AMP_MOVEXZ2, 2.0f); + //m_Force.z = 0.0009f; + m_GravityForce.y = -0.0005f; + angleFactor = 1.0472f; + angleMag = -0.001f; + doWind = true; + break; + // if (m_eAnchorPattern == AMP_NONE && m_fAnchorPreserveTime < 0.0f) + // MoveAnchorLine(AMP_MOVEXZ2, 2.0f); //m_GravityForce.y = (rand()%2+1)*-0.0015f; //TRACE("Apply force {}", m_Force.z); break; @@ -526,9 +542,17 @@ void CN3Cloak::TickByPlayerMotion(e_CloakMove eCurMove) default: break; } + if (doWind) + { + const int r = rand() % 10; + const float angle = (r * 0.03f + 0.7f) * angleFactor; + m_Force.x = 0.0f; + m_Force.y = sinf(angle) * angleMag; + m_Force.z = cosf(angle) * angleMag; + } } -void CN3Cloak::MoveAnchorLine(e_Cloak_AnchorMovePattern eType, float fPreserveTime) +/* void CN3Cloak::MoveAnchorLine(e_Cloak_AnchorMovePattern eType, float fPreserveTime) { if (m_eAnchorPattern != AMP_NONE) return; @@ -595,7 +619,7 @@ void CN3Cloak::MoveAnchorLine(e_Cloak_AnchorMovePattern eType, float fPreserveTi m_fAnchorPreserveTime = fPreserveTime; m_eAnchorPattern = eType; -} +}*/ void CN3Cloak::RestoreAnchorLine() { From ce6a4f535ab8076af699b7cd3232054afc06f02a Mon Sep 17 00:00:00 2001 From: reignsprime Date: Sun, 3 May 2026 10:44:14 +1000 Subject: [PATCH 6/7] Clean up CN3CPlugBase::Load --- src/N3Base/N3Chr.cpp | 87 +++++++++++++++----------------------------- src/N3Base/N3Chr.h | 13 +++---- 2 files changed, 35 insertions(+), 65 deletions(-) diff --git a/src/N3Base/N3Chr.cpp b/src/N3Base/N3Chr.cpp index dbbbbd408..d993f6805 100644 --- a/src/N3Base/N3Chr.cpp +++ b/src/N3Base/N3Chr.cpp @@ -306,6 +306,7 @@ CN3CPlugBase::~CN3CPlugBase() { s_MngTex.Delete(&m_pTexRef); s_MngTex.Delete(&m_pTexOverlapRef); + s_MngMesh.Delete(&m_pMesh); } void CN3CPlugBase::Release() @@ -316,6 +317,7 @@ void CN3CPlugBase::Release() s_MngTex.Delete(&m_pTexRef); s_MngTex.Delete(&m_pTexOverlapRef); + s_MngMesh.Delete(&m_pMesh); m_Mtl.Init(); m_vPosition.Zero(); @@ -463,7 +465,7 @@ bool CN3CPlugBase::Load(File& file) char szFN[512] = ""; file.Read(&m_ePlugType, 4); // Plug Type - //#ifdef _N3TOOL + //#ifdef _N3TOOL if (m_ePlugType > PLUGTYPE_MAX) { m_ePlugType = PLUGTYPE_NORMAL; @@ -481,8 +483,20 @@ bool CN3CPlugBase::Load(File& file) if (nL > 0) { file.Read(szFN, nL); - szFN[nL] = '\0'; - PMeshSet(szFN); + szFN[nL] = '\0'; + + char szExt[_MAX_EXT] = ""; + _splitpath_s(szFN, nullptr, 0, nullptr, 0, nullptr, 0, szExt, _MAX_EXT); + + if (_stricmp(szExt, ".n3pmesh") == 0) + { + PMeshSet(szFN); + } + else if (_stricmp(szExt, ".n3mesh") == 0) + { + s_MngMesh.Delete(&m_pMesh); + m_pMesh = s_MngMesh.Get(szFN); + } } file.Read(&nL, 4); @@ -514,13 +528,16 @@ bool CN3CPlugBase::Save(File& file) file.Write(&m_Mtl, sizeof(__Material)); // 재질 - nL = 0; - CN3PMesh* pPMesh = m_PMeshInst.GetMesh(); - if (pPMesh != nullptr) - nL = static_cast(pPMesh->FileName().size()); + nL = 0; + std::string szMeshFN; + if (CN3PMesh* pPMesh = m_PMeshInst.GetMesh()) + szMeshFN = pPMesh->FileName(); + else if (m_pMesh != nullptr) + szMeshFN = m_pMesh->FileName(); + nL = static_cast(szMeshFN.size()); file.Write(&nL, 4); if (nL > 0) - file.Write(pPMesh->FileName().c_str(), nL); + file.Write(szMeshFN.c_str(), nL); nL = 0; if (m_pTexRef != nullptr) @@ -1052,61 +1069,22 @@ void CN3CPlug_Cloak::Release() s_MngTex.Delete(&m_pTexClanMark); s_MngTex.Delete(&m_pTexPattern); #endif - s_MngMesh.Delete(&m_pMesh); CN3CPlugBase::Release(); } bool CN3CPlug_Cloak::Load(File& file) { - // Read the name header (CN3BaseFileAccess::Load), then re-parse all - // CN3CPlugBase fields manually so we can redirect the mesh load to - // s_MngMesh (CN3Mesh) instead of PMeshSet (CN3PMesh). Cloak assets are - // .n3mesh files; loading them as .n3pmesh produces garbage vertex counts. - CN3BaseFileAccess::Load(file); - - int nL = 0; - char szFN[512] = ""; - - file.Read(&m_ePlugType, 4); - if (m_ePlugType > PLUGTYPE_MAX) - m_ePlugType = PLUGTYPE_NORMAL; - file.Read(&m_nJointIndex, 4); - file.Read(&m_vPosition, sizeof(m_vPosition)); - file.Read(&m_MtxRot, sizeof(m_MtxRot)); - file.Read(&m_vScale, sizeof(m_vScale)); - file.Read(&m_Mtl, sizeof(__Material)); - - // Mesh: load as CN3Mesh, not CN3PMesh - file.Read(&nL, 4); - if (nL > 0) - { - file.Read(szFN, nL); - szFN[nL] = '\0'; - s_MngMesh.Delete(&m_pMesh); - m_pMesh = s_MngMesh.Get(szFN); - } - - // Texture - file.Read(&nL, 4); - if (nL > 0) - { - file.Read(szFN, nL); - szFN[nL] = '\0'; - TexSet(szFN); - } - - ReCalcMatrix(); + if (!CN3CPlugBase::Load(file)) + return false; - if (m_pMesh == nullptr) - return false; // mesh file missing. CloakPlugSet will clean up - return true; + return m_pMesh != nullptr; // mesh file missing or wrong extension } #ifdef _N3TOOL bool CN3CPlug_Cloak::Save(File& file) { CN3CPlugBase::Save(file); - return 0; + return false; } #endif void CN3CPlug_Cloak::Render(const __Matrix44& mtxParent, const __Matrix44& mtxJoint) @@ -1133,11 +1111,6 @@ void CN3CPlug_Cloak::SetLOD(int nLOD) bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, const std::string& sClanMarkTex, const std::string& sPatternTex) { - //Release(); - - // Release textures only — do NOT delete m_pMesh here, - // pMesh may point to the same mesh we currently hold - s_MngTex.Delete(&m_pTexColour); s_MngTex.Delete(&m_pTexClanMark); s_MngTex.Delete(&m_pTexPattern); @@ -1150,8 +1123,6 @@ bool CN3CPlug_Cloak::Init(CN3Mesh* pMesh, const std::string& sColourTex, //if (pMesh->IndexCount() != 216) // return false; - m_pMesh = pMesh; - m_pTexColour = s_MngTex.Get(sColourTex, true, 0); m_pTexClanMark = s_MngTex.Get(sClanMarkTex, true, 0); m_pTexPattern = s_MngTex.Get(sPatternTex, true, 0); diff --git a/src/N3Base/N3Chr.h b/src/N3Base/N3Chr.h index c665f4a08..6ebe30aa7 100644 --- a/src/N3Base/N3Chr.h +++ b/src/N3Base/N3Chr.h @@ -138,6 +138,7 @@ class CN3CPlugBase : public CN3BaseFileAccess protected: CN3PMeshInstance m_PMeshInst; // Progressive Mesh Instance + CN3Mesh* m_pMesh = nullptr; // Set when the .n3cplug references .n3mesh instead of .n3pmesh CN3Texture* m_pTexRef; // Texture Reference Pointer CN3Texture* m_pTexOverlapRef; // 위에 덧칠할 Texture Reference Pointer __Matrix44 m_MtxRot; // Rotation Matrix; @@ -228,6 +229,11 @@ class CN3CPlugBase : public CN3BaseFileAccess } void PMeshSet(const std::string& szFN); + + CN3Mesh* Mesh() + { + return m_pMesh; + } }; inline constexpr int MAX_PLUG_FX_POSITION = 5; @@ -296,16 +302,9 @@ class CN3CPlug_Cloak : public CN3CPlugBase #ifdef _N3GAME CN3Cloak m_Cloak; #endif - CN3Mesh* m_pMesh = nullptr; CN3Texture* m_pTexColour = nullptr; CN3Texture* m_pTexClanMark = nullptr; CN3Texture* m_pTexPattern = nullptr; - -public: - CN3Mesh* Mesh() - { - return m_pMesh; - } }; // 0 - 상체, 1 - 하체 ::: 관절들을 나누어서 나누어서 에니메이션 설정.. From eb8c8af3cc94b06ec5c84bdd085b72c009735c76 Mon Sep 17 00:00:00 2001 From: reignsprime Date: Sun, 3 May 2026 14:37:27 +1000 Subject: [PATCH 7/7] Ebenezer CUser::SendServerIndex --- src/Server/Ebenezer/User.cpp | 12 ++++++++++++ src/Server/Ebenezer/User.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/Server/Ebenezer/User.cpp b/src/Server/Ebenezer/User.cpp index 71659de00..f6d0e42f0 100644 --- a/src/Server/Ebenezer/User.cpp +++ b/src/Server/Ebenezer/User.cpp @@ -1893,6 +1893,16 @@ void CUser::Attack(char* pBuf) } } +void CUser::SendServerIndex() +{ + int index = 0; + char buf[8] {}; + SetByte(buf, WIZ_SERVER_INDEX, index); + SetShort(buf, 1, index); + SetShort(buf, m_pMain->m_nServerNo, index); + Send(buf, index); +} + void CUser::SendMyInfo(int type) { // TODO: @@ -14173,6 +14183,7 @@ void CUser::GameStart(char* pBuf) // Started loading if (opcode == 1) { + SendServerIndex(); SendMyInfo(0); m_pMain->UserInOutForMe(this); m_pMain->NpcInOutForMe(this); @@ -14231,6 +14242,7 @@ void CUser::GameStart(char* pBuf) // TODO: // BlinkStart(); + SetUserAbility(); // If there is a permanent chat available!!! diff --git a/src/Server/Ebenezer/User.h b/src/Server/Ebenezer/User.h index a9199e8d9..e9f345282 100644 --- a/src/Server/Ebenezer/User.h +++ b/src/Server/Ebenezer/User.h @@ -542,6 +542,7 @@ class CUser : public TcpServerSocket void Chat(char* pBuf); void LogOut(); void SelCharToAgent(char* pBuf); + void SendServerIndex(); void SendMyInfo(int type); void SelectCharacter(const char* pBuf); void Send2AI_UserUpdateInfo();