Merge pull request #40 from MindRapist/mr-3

MR-3: Bunch of fixes
This commit is contained in:
rtw1x1
2025-12-25 16:00:54 +00:00
committed by GitHub
9 changed files with 60 additions and 10 deletions

View File

@@ -10,7 +10,10 @@ This repository contains the source code necessary to compile the game client ex
--- ---
## Changelog 📋 ## 📋 Changelog
### 🐛 Bug Fixes ### 🐛 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.

View File

@@ -270,6 +270,7 @@ void CEffectInstance::Clear()
void CEffectInstance::__Initialize() void CEffectInstance::__Initialize()
{ {
ReleaseAlwaysHidden(); ReleaseAlwaysHidden();
m_isAlive = FALSE; m_isAlive = FALSE;
m_dwFrame = 0; m_dwFrame = 0;
m_pSoundInstanceVector = NULL; m_pSoundInstanceVector = NULL;

View File

@@ -484,15 +484,18 @@ DWORD CEffectManager::GetSelectedEffectDataCRC() const
return 0; return 0;
CEffectData* pData = m_pSelectedEffectInstance->GetEffectDataPointer(); CEffectData* pData = m_pSelectedEffectInstance->GetEffectDataPointer();
if (!pData) if (!pData)
return 0; return 0;
const char* cszFile = pData->GetFileName(); const char* cszFile = pData->GetFileName();
if (!cszFile || !cszFile[0]) if (!cszFile || !cszFile[0])
return 0; return 0;
std::string str; std::string str;
StringPath(cszFile, str); StringPath(cszFile, str);
return GetCaseCRC32(str.c_str(), (int)str.length()); return GetCaseCRC32(str.c_str(), (int)str.length());
} }

View File

@@ -285,9 +285,7 @@ void CGraphicObjectInstance::Initialize()
m_isVisible = TRUE; m_isVisible = TRUE;
m_BlockCamera = false; m_BlockCamera = false;
m_isAlwaysHidden = false; m_isAlwaysHidden = false;
m_v3Position.x = m_v3Position.y = m_v3Position.z = 0.0f; m_v3Position.x = m_v3Position.y = m_v3Position.z = 0.0f;
m_v3Scale.x = m_v3Scale.y = m_v3Scale.z = 1.0f; m_v3Scale.x = m_v3Scale.y = m_v3Scale.z = 1.0f;

View File

@@ -60,7 +60,6 @@ class CGraphicObjectInstance : public CGraphicCollisionObject
void ApplyAlwaysHidden(); void ApplyAlwaysHidden();
void ReleaseAlwaysHidden(); void ReleaseAlwaysHidden();
// Camera Block // Camera Block
void BlockCamera(bool bBlock) {m_BlockCamera = bBlock;} void BlockCamera(bool bBlock) {m_BlockCamera = bBlock;}
bool BlockCamera() { return m_BlockCamera; } bool BlockCamera() { return m_BlockCamera; }

View File

@@ -362,6 +362,7 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject
BOOL IsActEmotion(); BOOL IsActEmotion();
DWORD GetComboIndex(); DWORD GetComboIndex();
float GetAttackingElapsedTime(); float GetAttackingElapsedTime();
void SetBlendingPosition(const TPixelPosition & c_rPosition, float fBlendingTime = 1.0f); void SetBlendingPosition(const TPixelPosition & c_rPosition, float fBlendingTime = 1.0f);
void ResetBlendingPosition(); void ResetBlendingPosition();
void GetBlendingPosition(TPixelPosition * pPosition); void GetBlendingPosition(TPixelPosition * pPosition);
@@ -607,6 +608,9 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject
void __ProcessMotionEventAttackSuccess(DWORD dwMotionKey, BYTE byEventIndex, CActorInstance & rVictim); void __ProcessMotionEventAttackSuccess(DWORD dwMotionKey, BYTE byEventIndex, CActorInstance & rVictim);
void __ProcessMotionAttackSuccess(DWORD dwMotionKey, 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 __HitStone(CActorInstance& rVictim);
void __HitGood(CActorInstance& rVictim); void __HitGood(CActorInstance& rVictim);

View File

@@ -620,19 +620,21 @@ void CActorInstance::__ProcessDataAttackSuccess(const NRaceData::TAttackData & c
// VICTIM_COLLISION_TEST_END // VICTIM_COLLISION_TEST_END
} }
// MR-3: Shaman on-mount hitting fix
// Invisible Time // Invisible Time
if (IS_PARTY_HUNTING_RACE(rVictim.GetRace())) if (IS_PARTY_HUNTING_RACE(rVictim.GetRace()))
{ {
if (uiSkill) // 파티 사냥 몬스터라도 스킬이면 무적시간 적용 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] 폴리모프 - 밸런싱 문제 타인 공격에 의한 무적 타임은 고려하지 않고 자신 공격에 의한것만 체크한다 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 // 파티 사냥 몬스터가 아닐 경우만 적용 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 // Stiffen Time
rVictim.InsertDelay(c_rAttackData.fStiffenTime); rVictim.InsertDelay(c_rAttackData.fStiffenTime);
@@ -964,3 +966,19 @@ void CActorInstance::__SetFallingDirection(float fx, float fy)
{ {
m_PhysicsObject.SetDirection(D3DXVECTOR3(fx, fy, 0.0f)); 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

View File

@@ -1978,9 +1978,10 @@ void CInstanceBase::Render()
if (pkInstEach) if (pkInstEach)
{ {
// MR-3: Invisibility fix
if (pkInstEach->IsAffect(AFFECT_INVISIBILITY) || pkInstEach->IsAffect(AFFECT_EUNHYEONG) || pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY)) 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; continue;
if (pkInstEach->IsAffect(AFFECT_EUNHYEONG) && !pkInstEach->IsAffect(AFFECT_INVISIBILITY) && !pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY)) if (pkInstEach->IsAffect(AFFECT_EUNHYEONG) && !pkInstEach->IsAffect(AFFECT_INVISIBILITY) && !pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY))
@@ -1988,6 +1989,11 @@ void CInstanceBase::Render()
else else
pkInstEach->m_GraphicThingInstance.HideAllAttachingEffect(); pkInstEach->m_GraphicThingInstance.HideAllAttachingEffect();
} }
else
{
pkInstEach->m_GraphicThingInstance.ShowAllAttachingEffect();
}
// MR-3: -- END OF -- Invisibility fix
} }
} }

View File

@@ -1662,18 +1662,36 @@ PyObject * skillGetSkillAffectDescription(PyObject * poSelf, PyObject * poArgs)
PyObject * skillGetSkillCoolTime(PyObject * poSelf, PyObject * poArgs) PyObject * skillGetSkillCoolTime(PyObject * poSelf, PyObject * poArgs)
{ {
int iSkillIndex; int iSkillIndex;
if (!PyTuple_GetInteger(poArgs, 0, &iSkillIndex)) if (!PyTuple_GetInteger(poArgs, 0, &iSkillIndex))
return Py_BadArgument(); return Py_BadArgument();
float fSkillPoint; float fSkillPoint;
if (!PyTuple_GetFloat(poArgs, 1, &fSkillPoint)) if (!PyTuple_GetFloat(poArgs, 1, &fSkillPoint))
return Py_BadArgument(); return Py_BadArgument();
CPythonSkill::SSkillData * c_pSkillData; CPythonSkill::SSkillData * c_pSkillData;
if (!CPythonSkill::Instance().GetSkillData(iSkillIndex, &c_pSkillData)) if (!CPythonSkill::Instance().GetSkillData(iSkillIndex, &c_pSkillData))
return Py_BuildException("skill.GetSkillCoolTime - Failed to find skill by %d", iSkillIndex); 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) PyObject * skillGetSkillNeedSP(PyObject * poSelf, PyObject * poArgs)