From ef7c946cfba3a02d15e041dd20f9f44f4e421919 Mon Sep 17 00:00:00 2001 From: Mind Rapist Date: Thu, 25 Dec 2025 08:39:58 +0200 Subject: [PATCH 1/2] MR-3: Bunch of fixes --- README.md | 7 +++++-- src/EffectLib/EffectInstance.cpp | 1 + src/EffectLib/EffectManager.cpp | 3 +++ src/EterLib/GrpObjectInstance.cpp | 2 -- src/EterLib/GrpObjectInstance.h | 1 - src/GameLib/ActorInstance.h | 30 +++++++++++++++++++++++++++++ src/GameLib/ActorInstanceBattle.cpp | 24 ++++++++++++++++++++--- src/UserInterface/InstanceBase.cpp | 16 ++++++++++++++- src/UserInterface/PythonSkill.cpp | 20 ++++++++++++++++++- 9 files changed, 94 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 15df256..1d1ca5f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,10 @@ This repository contains the source code necessary to compile the game client ex --- -## Changelog 📋 +## 📋 Changelog ### 🐛 Bug Fixes -* **Realtime Character Level Updates:** Implemented the reversed fix (credits to Mali) for updating character level in real-time across game view and all windows (such as Guild window) for all viewing players. +* **Shaman Mounted Combat:** Fixed a bug that wrongly calculated Shaman characters mounted hits that didn't collide with the target to cause damage when attack speed was had an extremely high value. +* **Invisibility VFX Logic:** Fixed an issue where skill visual effects remained visible to the character while under the `AFFECT_INVISIBLE` state. +* **Buff Effects Visibility Recovery:** Fixed an issue where buff skill visual effects remained invisible if the skill was cast while the character was under the `AFFECT_INVISIBLE` state. +* **Casting Speed Cooldowns:** Fixed an issue where Casting Speed was not properly calculated in skill cooldowns. The system now supports real-time calculation updates whenever the bonus value changes. diff --git a/src/EffectLib/EffectInstance.cpp b/src/EffectLib/EffectInstance.cpp index 9d0e522..b542378 100644 --- a/src/EffectLib/EffectInstance.cpp +++ b/src/EffectLib/EffectInstance.cpp @@ -270,6 +270,7 @@ void CEffectInstance::Clear() void CEffectInstance::__Initialize() { ReleaseAlwaysHidden(); + m_isAlive = FALSE; m_dwFrame = 0; m_pSoundInstanceVector = NULL; diff --git a/src/EffectLib/EffectManager.cpp b/src/EffectLib/EffectManager.cpp index b32329d..2037c39 100644 --- a/src/EffectLib/EffectManager.cpp +++ b/src/EffectLib/EffectManager.cpp @@ -484,15 +484,18 @@ DWORD CEffectManager::GetSelectedEffectDataCRC() const return 0; CEffectData* pData = m_pSelectedEffectInstance->GetEffectDataPointer(); + if (!pData) return 0; const char* cszFile = pData->GetFileName(); + if (!cszFile || !cszFile[0]) return 0; std::string str; StringPath(cszFile, str); + return GetCaseCRC32(str.c_str(), (int)str.length()); } diff --git a/src/EterLib/GrpObjectInstance.cpp b/src/EterLib/GrpObjectInstance.cpp index 0cec621..2cce95e 100644 --- a/src/EterLib/GrpObjectInstance.cpp +++ b/src/EterLib/GrpObjectInstance.cpp @@ -285,9 +285,7 @@ void CGraphicObjectInstance::Initialize() m_isVisible = TRUE; m_BlockCamera = false; - m_isAlwaysHidden = false; - m_v3Position.x = m_v3Position.y = m_v3Position.z = 0.0f; m_v3Scale.x = m_v3Scale.y = m_v3Scale.z = 1.0f; diff --git a/src/EterLib/GrpObjectInstance.h b/src/EterLib/GrpObjectInstance.h index 297c0de..8078ca9 100644 --- a/src/EterLib/GrpObjectInstance.h +++ b/src/EterLib/GrpObjectInstance.h @@ -60,7 +60,6 @@ class CGraphicObjectInstance : public CGraphicCollisionObject void ApplyAlwaysHidden(); void ReleaseAlwaysHidden(); - // Camera Block void BlockCamera(bool bBlock) {m_BlockCamera = bBlock;} bool BlockCamera() { return m_BlockCamera; } diff --git a/src/GameLib/ActorInstance.h b/src/GameLib/ActorInstance.h index ee6bb3e..55f7ebe 100644 --- a/src/GameLib/ActorInstance.h +++ b/src/GameLib/ActorInstance.h @@ -362,10 +362,22 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject BOOL IsActEmotion(); DWORD GetComboIndex(); float GetAttackingElapsedTime(); +#ifdef FIX_POS_SYNC + void SetBlendingPosition(const TPixelPosition& c_rPosition, float fBlendingTime = 1.0f); +#else void SetBlendingPosition(const TPixelPosition & c_rPosition, float fBlendingTime = 1.0f); +#endif void ResetBlendingPosition(); void GetBlendingPosition(TPixelPosition * pPosition); +#ifdef FIX_POS_SYNC + struct BlendingPosition { + D3DXVECTOR3 source; + D3DXVECTOR3 dest; + float duration; + }; +#endif + BOOL NormalAttack(float fDirRot, float fBlendTime = 0.1f); BOOL ComboAttack(DWORD wMotionIndex, float fDirRot, float fBlendTime = 0.1f); @@ -479,6 +491,13 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject void RenderCollisionData(); void RenderToShadowMap(); +#ifdef FIX_POS_SYNC + void ClientAttack(DWORD dwVID); + void ServerAttack(DWORD dwVID); + bool ProcessingClientAttack(DWORD dwVID); + bool ServerAttackCameFirst(DWORD dwVID); +#endif + protected: void __AdjustCollisionMovement(const CGraphicObjectInstance * c_pGraphicObjectInstance); @@ -603,14 +622,25 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject void __ClearCombo(); void __OnEndCombo(); +#ifdef FIX_POS_SYNC + void __Push(const TPixelPosition& c_rkPPosDst, unsigned int unDuration); +#endif + void __ProcessDataAttackSuccess(const NRaceData::TAttackData & c_rAttackData, CActorInstance & rVictim, const D3DXVECTOR3 & c_rv3Position, UINT uiSkill = 0, BOOL isSendPacket = TRUE); void __ProcessMotionEventAttackSuccess(DWORD dwMotionKey, BYTE byEventIndex, CActorInstance & rVictim); void __ProcessMotionAttackSuccess(DWORD dwMotionKey, CActorInstance & rVictim); + // MR-3: Shaman on-mount hitting fix + float __GetInvisibleTimeAdjust(const UINT uiSkill, const NRaceData::TAttackData& c_rAttackData); + // MR-3: -- END OF -- Shaman on-mount hitting fix void __HitStone(CActorInstance& rVictim); void __HitGood(CActorInstance& rVictim); +#ifdef FIX_POS_SYNC + void __HitGreate(CActorInstance& rVictim, UINT uiSkill); +#else void __HitGreate(CActorInstance& rVictim); +#endif void __PushDirect(CActorInstance & rVictim); void __PushCircle(CActorInstance & rVictim); diff --git a/src/GameLib/ActorInstanceBattle.cpp b/src/GameLib/ActorInstanceBattle.cpp index 6f236e7..c5aba19 100644 --- a/src/GameLib/ActorInstanceBattle.cpp +++ b/src/GameLib/ActorInstanceBattle.cpp @@ -620,19 +620,21 @@ void CActorInstance::__ProcessDataAttackSuccess(const NRaceData::TAttackData & c // VICTIM_COLLISION_TEST_END } + // MR-3: Shaman on-mount hitting fix // Invisible Time if (IS_PARTY_HUNTING_RACE(rVictim.GetRace())) { if (uiSkill) // 파티 사냥 몬스터라도 스킬이면 무적시간 적용 - rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + c_rAttackData.fInvisibleTime; + rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + (c_rAttackData.fInvisibleTime - __GetInvisibleTimeAdjust(uiSkill, c_rAttackData)); if (m_isMain) // #0000794: [M2KR] 폴리모프 - 밸런싱 문제 타인 공격에 의한 무적 타임은 고려하지 않고 자신 공격에 의한것만 체크한다 - rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + c_rAttackData.fInvisibleTime; + rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + (c_rAttackData.fInvisibleTime - __GetInvisibleTimeAdjust(uiSkill, c_rAttackData)); } else // 파티 사냥 몬스터가 아닐 경우만 적용 { - rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + c_rAttackData.fInvisibleTime; + rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + (c_rAttackData.fInvisibleTime - __GetInvisibleTimeAdjust(uiSkill, c_rAttackData)); } + // MR-3: -- END OF -- Shaman on-mount hitting fix // Stiffen Time rVictim.InsertDelay(c_rAttackData.fStiffenTime); @@ -964,3 +966,19 @@ void CActorInstance::__SetFallingDirection(float fx, float fy) { m_PhysicsObject.SetDirection(D3DXVECTOR3(fx, fy, 0.0f)); } + +// MR-3: Shaman on-mount hitting fix +float CActorInstance::__GetInvisibleTimeAdjust(const UINT uiSkill, const NRaceData::TAttackData& c_rAttackData) { + + static const int shamanw = 3, shamanm = 7; + + if ((GetRace() != shamanw && GetRace() != shamanm) || + uiSkill != 0 || + m_fAtkSpd < 1.3) + return 0.0f; + + const auto scale = (m_fAtkSpd - 1.3) / 1.3; + const auto inv = c_rAttackData.fInvisibleTime * 0.5; + return inv * scale; +} +// MR-3: -- END OF -- Shaman on-mount hitting fix diff --git a/src/UserInterface/InstanceBase.cpp b/src/UserInterface/InstanceBase.cpp index 5adbb32..07280b3 100644 --- a/src/UserInterface/InstanceBase.cpp +++ b/src/UserInterface/InstanceBase.cpp @@ -1556,7 +1556,11 @@ void CInstanceBase::StateProcess() SetAdvancingRotation(fRotDst); SetRotation(fRotDst); +#ifdef FIX_POS_SYNC + NEW_UseSkill(1, eFunc& FUNC_SKILL - 1, uArg & 0x0f, (uArg >> 4) ? true : false); +#else NEW_UseSkill(0, eFunc & 0x7f, uArg&0x0f, (uArg>>4) ? true : false); +#endif //Tracen("가깝기 때문에 워프 공격"); } } @@ -1734,7 +1738,11 @@ void CInstanceBase::MovementProcess() { SetAdvancingRotation(m_fDstRot); BlendRotation(m_fDstRot); +#ifdef FIX_POS_SYNC + NEW_UseSkill(1, m_kMovAfterFunc.eFunc& FUNC_SKILL - 1, m_kMovAfterFunc.uArg & 0x0f, (m_kMovAfterFunc.uArg >> 4) ? true : false); +#else NEW_UseSkill(0, m_kMovAfterFunc.eFunc & 0x7f, m_kMovAfterFunc.uArg&0x0f, (m_kMovAfterFunc.uArg>>4) ? true : false); +#endif } else { @@ -1978,9 +1986,10 @@ void CInstanceBase::Render() if (pkInstEach) { + // MR-3: Invisibility fix if (pkInstEach->IsAffect(AFFECT_INVISIBILITY) || pkInstEach->IsAffect(AFFECT_EUNHYEONG) || pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY)) { - if (CPythonPlayer::Instance().IsMainCharacterIndex(pkInstEach->GetVirtualID())) + if (CPythonPlayer::Instance().IsMainCharacterIndex(pkInstEach->GetVirtualID()) && !pkInstEach->IsAffect(AFFECT_INVISIBILITY)) continue; if (pkInstEach->IsAffect(AFFECT_EUNHYEONG) && !pkInstEach->IsAffect(AFFECT_INVISIBILITY) && !pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY)) @@ -1988,6 +1997,11 @@ void CInstanceBase::Render() else pkInstEach->m_GraphicThingInstance.HideAllAttachingEffect(); } + else + { + pkInstEach->m_GraphicThingInstance.ShowAllAttachingEffect(); + } + // MR-3: -- END OF -- Invisibility fix } } diff --git a/src/UserInterface/PythonSkill.cpp b/src/UserInterface/PythonSkill.cpp index b5d2eb2..53c0e21 100644 --- a/src/UserInterface/PythonSkill.cpp +++ b/src/UserInterface/PythonSkill.cpp @@ -1662,18 +1662,36 @@ PyObject * skillGetSkillAffectDescription(PyObject * poSelf, PyObject * poArgs) PyObject * skillGetSkillCoolTime(PyObject * poSelf, PyObject * poArgs) { int iSkillIndex; + if (!PyTuple_GetInteger(poArgs, 0, &iSkillIndex)) return Py_BadArgument(); float fSkillPoint; + if (!PyTuple_GetFloat(poArgs, 1, &fSkillPoint)) return Py_BadArgument(); CPythonSkill::SSkillData * c_pSkillData; + if (!CPythonSkill::Instance().GetSkillData(iSkillIndex, &c_pSkillData)) return Py_BuildException("skill.GetSkillCoolTime - Failed to find skill by %d", iSkillIndex); - return Py_BuildValue("i", c_pSkillData->GetSkillCoolTime(fSkillPoint)); + // MR-3: Calculate casting speed bonus on skill cool time + DWORD dwSkillCoolTime = c_pSkillData->GetSkillCoolTime(fSkillPoint); + int iCastingSpeed = CPythonPlayer::Instance().GetStatus(POINT_CASTING_SPEED); + + int iSpd = 100 - iCastingSpeed; + if (iSpd > 0) + iSpd = 100 + iSpd; + else if (iSpd < 0) + iSpd = 10000 / (100 - iSpd); + else + iSpd = 100; + + dwSkillCoolTime = dwSkillCoolTime * iSpd / 100; + + return Py_BuildValue("i", dwSkillCoolTime); + // MR-3: -- END OF -- Calculate casting speed bonus on skill cool time } PyObject * skillGetSkillNeedSP(PyObject * poSelf, PyObject * poArgs) From 99bd5103a3952599234d0aefa3dbcc46e1d87106 Mon Sep 17 00:00:00 2001 From: Mind Rapist Date: Thu, 25 Dec 2025 17:51:09 +0200 Subject: [PATCH 2/2] MR-3: Bunch of fixes --- src/GameLib/ActorInstance.h | 28 +--------------------------- src/UserInterface/InstanceBase.cpp | 8 -------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/src/GameLib/ActorInstance.h b/src/GameLib/ActorInstance.h index 55f7ebe..d1fc556 100644 --- a/src/GameLib/ActorInstance.h +++ b/src/GameLib/ActorInstance.h @@ -362,22 +362,11 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject BOOL IsActEmotion(); DWORD GetComboIndex(); float GetAttackingElapsedTime(); -#ifdef FIX_POS_SYNC - void SetBlendingPosition(const TPixelPosition& c_rPosition, float fBlendingTime = 1.0f); -#else + void SetBlendingPosition(const TPixelPosition & c_rPosition, float fBlendingTime = 1.0f); -#endif void ResetBlendingPosition(); void GetBlendingPosition(TPixelPosition * pPosition); -#ifdef FIX_POS_SYNC - struct BlendingPosition { - D3DXVECTOR3 source; - D3DXVECTOR3 dest; - float duration; - }; -#endif - BOOL NormalAttack(float fDirRot, float fBlendTime = 0.1f); BOOL ComboAttack(DWORD wMotionIndex, float fDirRot, float fBlendTime = 0.1f); @@ -491,13 +480,6 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject void RenderCollisionData(); void RenderToShadowMap(); -#ifdef FIX_POS_SYNC - void ClientAttack(DWORD dwVID); - void ServerAttack(DWORD dwVID); - bool ProcessingClientAttack(DWORD dwVID); - bool ServerAttackCameFirst(DWORD dwVID); -#endif - protected: void __AdjustCollisionMovement(const CGraphicObjectInstance * c_pGraphicObjectInstance); @@ -622,10 +604,6 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject void __ClearCombo(); void __OnEndCombo(); -#ifdef FIX_POS_SYNC - void __Push(const TPixelPosition& c_rkPPosDst, unsigned int unDuration); -#endif - void __ProcessDataAttackSuccess(const NRaceData::TAttackData & c_rAttackData, CActorInstance & rVictim, const D3DXVECTOR3 & c_rv3Position, UINT uiSkill = 0, BOOL isSendPacket = TRUE); void __ProcessMotionEventAttackSuccess(DWORD dwMotionKey, BYTE byEventIndex, CActorInstance & rVictim); void __ProcessMotionAttackSuccess(DWORD dwMotionKey, CActorInstance & rVictim); @@ -636,11 +614,7 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject void __HitStone(CActorInstance& rVictim); void __HitGood(CActorInstance& rVictim); -#ifdef FIX_POS_SYNC - void __HitGreate(CActorInstance& rVictim, UINT uiSkill); -#else void __HitGreate(CActorInstance& rVictim); -#endif void __PushDirect(CActorInstance & rVictim); void __PushCircle(CActorInstance & rVictim); diff --git a/src/UserInterface/InstanceBase.cpp b/src/UserInterface/InstanceBase.cpp index 07280b3..81e41a3 100644 --- a/src/UserInterface/InstanceBase.cpp +++ b/src/UserInterface/InstanceBase.cpp @@ -1556,11 +1556,7 @@ void CInstanceBase::StateProcess() SetAdvancingRotation(fRotDst); SetRotation(fRotDst); -#ifdef FIX_POS_SYNC - NEW_UseSkill(1, eFunc& FUNC_SKILL - 1, uArg & 0x0f, (uArg >> 4) ? true : false); -#else NEW_UseSkill(0, eFunc & 0x7f, uArg&0x0f, (uArg>>4) ? true : false); -#endif //Tracen("가깝기 때문에 워프 공격"); } } @@ -1738,11 +1734,7 @@ void CInstanceBase::MovementProcess() { SetAdvancingRotation(m_fDstRot); BlendRotation(m_fDstRot); -#ifdef FIX_POS_SYNC - NEW_UseSkill(1, m_kMovAfterFunc.eFunc& FUNC_SKILL - 1, m_kMovAfterFunc.uArg & 0x0f, (m_kMovAfterFunc.uArg >> 4) ? true : false); -#else NEW_UseSkill(0, m_kMovAfterFunc.eFunc & 0x7f, m_kMovAfterFunc.uArg&0x0f, (m_kMovAfterFunc.uArg>>4) ? true : false); -#endif } else {