forked from metin-server/m2dev-client-src
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user