diff --git a/.gitignore b/.gitignore index 80a1eda..6b1448a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,63 @@ /build/ + +# ======================================================= +# FULLY CASE-INSENSITIVE BACKUP EXCLUSIONS +# Matches all files and folders ending in: +# _BK, _BAK, .BK, .BAK (and all case permutations) +# ======================================================= + +# ------------------------------------------------------- +# 1. EXCLUSIONS ENDING IN .BK / _BK +# ------------------------------------------------------- + +# Files/Folders ending in _BK (e.g., File_Bk, Folder_bK) +*_BK +*_Bk +*_bK +*_bk + +# Files ending in .BK (e.g., File.BK, File.bK) +*.BK +*.Bk +*.bK +*.bk + +# Files ending in "double extension" .X.BK (e.g., File.txt.Bk) +*.*.BK +*.*.Bk +*.*.bK +*.*.bk + +# ------------------------------------------------------- +# 2. EXCLUSIONS ENDING IN .BAK / _BAK +# ------------------------------------------------------- + +# Files/Folders ending in _BAK (e.g., File_BAK, Folder_bak) +*_BAK +*_BAk +*_BaK +*_Bak +*_bAK +*_bAk +*_baK +*_bak + +# Files ending in .BAK (e.g., File.BAK, File.bak) +*.BAK +*.BAk +*.BaK +*.Bak +*.bAK +*.bAk +*.baK +*.bak + +# Files ending in "double extension" .X.BAK (e.g., File.txt.Bak) +*.*.BAK +*.*.BAk +*.*.BaK +*.*.Bak +*.*.bAK +*.*.bAk +*.*.baK +*.*.bak diff --git a/README.md b/README.md index 8427814..1166813 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,20 @@ This repository contains the source code necessary to compile the game client ex --- -## ✨ Key Source Code Fixes +## Changelog 📋 -The following fixes address critical issues related to locale compilation and texture loading, ensuring compatibility with new content and correct language output. +### 🐛 Bug Fixes +* **Character Selection:** Corrected the display of character stats and ensured gauge bars accurately reflect the character's stats during the selection phase. +* **Changing skill group:** Fixed an issue involving incorrect skill dictionary clearing/restructuring when changing a skill group. This previously caused the client to be unable to find the correct skill slot after a skill group reset, preventing the display of newly added points on level-up and/or clearing cooldown and active state when used and applied new point (if clearing cooldowns for level-0 skills enabled). -### 🖼️ Texture and Resource Loading Fix - -* **Fix: Missing Texture Cache:** Resolved an issue in `UserInterface/UserInterface.cpp` by adding the necessary missing resource directory names (`"_texcache"`) within the `PackInitialize` function. - * **Impact:** This ensures that texture files for mobs and terrain for the new maps (Cape, Bay, Dawn, Thunder) are displayed. - -### 🌐 Build Configuration Fix - -* **Fix: Locale Build Typos:** Corrected a critical typo in `UserInterface/locale.cpp` where the `RelWithDebInfo` build configuration incorrectly referenced `tr (1253)`. - * **Impact:** The value was updated to **`gr (1253)`** to correctly reference the Greek locale option before game launch. +### ⬆️ Feature Improvements +* **Messenger System:** + * **Live Removal Updates:** The client now receives immediate live updates when a player is removed by a friend, improving synchronization. +* **Skill Cooldowns and States:** Enhanced reliability and persistence across numerous client actions: + * **Persistence:** Cooldowns and active slot effects are now correctly maintained when changing the Character Window view, switching skill tabs, mounting/unmounting, leveling up a skill or relogging. This persistence is ensured for togglable, non-togglable, and togglable skills with cooldowns. + * **Grade Transfer:** Cooldowns and active slot states are correctly transferred when a skill's grade is changed (e.g., to Master or Perfect). + * **Reset Logic:** Skill cooldowns are cleared and/or active slots are disabled when a skill is reset (individually or via a skill group reset). + * **Support Skills:** Support skill levels are correctly maintained when the active skill group is changed/reset. + * **Horse Skills:** Implemented special handling to ensure horse skill cooldowns are cleared when their level is changed to 0 (via commands like `/setsk`). + * **Combo Skills:** Combo skills are automatically disabled if their level is changed to 0 (via commands like `/setsk`). +* **.gitignore file:** Ignoring all files and directories ending in `_BAK` or `.BAK` (case-insensitive) diff --git a/src/EffectLib/EffectInstance.cpp b/src/EffectLib/EffectInstance.cpp index 07c911e..0c9d4bf 100644 --- a/src/EffectLib/EffectInstance.cpp +++ b/src/EffectLib/EffectInstance.cpp @@ -269,6 +269,9 @@ void CEffectInstance::Clear() void CEffectInstance::__Initialize() { +#ifdef __ENABLE_STEALTH_FIX__ + ReleaseAlwaysHidden(); +#endif m_isAlive = FALSE; m_dwFrame = 0; m_pSoundInstanceVector = NULL; diff --git a/src/EffectLib/EffectInstance.h b/src/EffectLib/EffectInstance.h index a382d49..7a1fcb2 100644 --- a/src/EffectLib/EffectInstance.h +++ b/src/EffectLib/EffectInstance.h @@ -4,6 +4,7 @@ #include "Eterlib/Pool.h" #include "AudioLib/Type.h" +#include "StdAfx.h" #include "EffectElementBaseInstance.h" #include "EffectData.h" #include "EffectMeshInstance.h" @@ -41,6 +42,14 @@ class CEffectInstance : public CGraphicObjectInstance bool LessRenderOrder(CEffectInstance* pkEftInst); void SetEffectDataPointer(CEffectData * pEffectData); + +#ifdef __ENABLE_STEALTH_FIX__ //EXP + // Returns the pointer to the effect data associated with this instance. + CEffectData* GetEffectDataPointer() const + { + return m_pkEftData; + } +#endif void Clear(); BOOL isAlive(); @@ -85,6 +94,7 @@ class CEffectInstance : public CGraphicObjectInstance float m_fLastTime; public: + static CDynamicPool ms_kPool; static int ms_iRenderingEffectCount; }; diff --git a/src/EffectLib/EffectManager.cpp b/src/EffectLib/EffectManager.cpp index 294e72d..3c5e936 100644 --- a/src/EffectLib/EffectManager.cpp +++ b/src/EffectLib/EffectManager.cpp @@ -351,6 +351,24 @@ void CEffectManager::HideEffect() m_pSelectedEffectInstance->Hide(); } +#ifdef __ENABLE_STEALTH_FIX__ +void CEffectManager::ApplyAlwaysHidden() +{ + if (!m_pSelectedEffectInstance) + return; + + m_pSelectedEffectInstance->ApplyAlwaysHidden(); +} + +void CEffectManager::ReleaseAlwaysHidden() +{ + if (!m_pSelectedEffectInstance) + return; + + m_pSelectedEffectInstance->ReleaseAlwaysHidden(); +} +#endif + bool CEffectManager::GetEffectData(DWORD dwID, CEffectData ** ppEffect) { TEffectDataMap::iterator itor = m_kEftDataMap.find(dwID); @@ -462,5 +480,25 @@ CEffectManager::~CEffectManager() Destroy(); } +#ifdef __ENABLE_STEALTH_FIX__ //EXP +DWORD CEffectManager::GetSelectedEffectDataCRC() const +{ + if (!m_pSelectedEffectInstance) + 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()); +} +#endif + // just for map effect diff --git a/src/EffectLib/EffectManager.h b/src/EffectLib/EffectManager.h index a395a85..b04fb09 100644 --- a/src/EffectLib/EffectManager.h +++ b/src/EffectLib/EffectManager.h @@ -1,5 +1,6 @@ #pragma once +#include "StdAfx.h" #include "EffectInstance.h" class CEffectManager : public CScreen, public CSingleton @@ -56,6 +57,11 @@ class CEffectManager : public CScreen, public CSingleton void ShowEffect(); void HideEffect(); +#ifdef __ENABLE_STEALTH_FIX__ + void ApplyAlwaysHidden(); + void ReleaseAlwaysHidden(); +#endif + // Temporary function DWORD GetRandomEffect(); int GetEmptyIndex(); @@ -69,6 +75,11 @@ class CEffectManager : public CScreen, public CSingleton int GetRenderingEffectCount(); +#ifdef __ENABLE_STEALTH_FIX__ //EXP + // Return the CRC of the effect-data for the selected effect instance. + DWORD GetSelectedEffectDataCRC() const; +#endif + protected: void __Initialize(); diff --git a/src/EffectLib/StdAfx.h b/src/EffectLib/StdAfx.h index bacf045..a8fd469 100644 --- a/src/EffectLib/StdAfx.h +++ b/src/EffectLib/StdAfx.h @@ -14,6 +14,8 @@ #include "AudioLib/StdAfx.h" +#include "UserInterface/Locale_inc.h" + /* #include "FrameController.h" diff --git a/src/EterLib/GrpObjectInstance.cpp b/src/EterLib/GrpObjectInstance.cpp index 6e9ba11..12ce158 100644 --- a/src/EterLib/GrpObjectInstance.cpp +++ b/src/EterLib/GrpObjectInstance.cpp @@ -200,9 +200,24 @@ void CGraphicObjectInstance::Hide() { m_isVisible = false; } + +#ifdef __ENABLE_STEALTH_FIX__ +void CGraphicObjectInstance::ApplyAlwaysHidden() { + m_isAlwaysHidden = true; +} + +void CGraphicObjectInstance::ReleaseAlwaysHidden() { + m_isAlwaysHidden = false; +} +#endif + bool CGraphicObjectInstance::isShow() { +#ifdef __ENABLE_STEALTH_FIX__ + return m_isVisible && !m_isAlwaysHidden; +#else return m_isVisible; +#endif } // @@ -276,6 +291,11 @@ void CGraphicObjectInstance::Initialize() m_isVisible = TRUE; m_BlockCamera = false; + +#ifdef __ENABLE_STEALTH_FIX__ + m_isAlwaysHidden = false; +#endif + 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 707af30..7714536 100644 --- a/src/EterLib/GrpObjectInstance.h +++ b/src/EterLib/GrpObjectInstance.h @@ -57,6 +57,12 @@ class CGraphicObjectInstance : public CGraphicCollisionObject void Hide(); bool isShow(); +#ifdef __ENABLE_STEALTH_FIX__ + void ApplyAlwaysHidden(); + void ReleaseAlwaysHidden(); +#endif + + // Camera Block void BlockCamera(bool bBlock) {m_BlockCamera = bBlock;} bool BlockCamera() { return m_BlockCamera; } @@ -109,6 +115,10 @@ class CGraphicObjectInstance : public CGraphicCollisionObject D3DXMATRIX m_mRotation; bool m_isVisible; +#ifdef __ENABLE_STEALTH_FIX__ + bool m_isAlwaysHidden; +#endif + D3DXMATRIX m_worldMatrix; // Camera Block diff --git a/src/EterLib/StdAfx.h b/src/EterLib/StdAfx.h index c7aef1b..bf01e77 100644 --- a/src/EterLib/StdAfx.h +++ b/src/EterLib/StdAfx.h @@ -36,6 +36,8 @@ #include "EterBase/Debug.h" #include "EterLocale/CodePageId.h" +#include "UserInterface/Locale_inc.h" + #ifndef VC_EXTRALEAN #include #endif diff --git a/src/EterPythonLib/PythonSlotWindow.cpp b/src/EterPythonLib/PythonSlotWindow.cpp index a64c34b..824252e 100644 --- a/src/EterPythonLib/PythonSlotWindow.cpp +++ b/src/EterPythonLib/PythonSlotWindow.cpp @@ -4,6 +4,11 @@ #include "PythonWindow.h" #include "PythonSlotWindow.h" +#ifdef FIX_REFRESH_SKILL_COOLDOWN +#include "UserInterface/PythonSkill.h" +#include "UserInterface/PythonPlayer.h" +#endif + //#define __RENDER_SLOT_AREA__ using namespace UI; @@ -204,6 +209,14 @@ void CSlotWindow::SetSlotType(DWORD dwType) m_dwSlotType = dwType; } +#ifdef FIX_REFRESH_SKILL_COOLDOWN +DWORD CSlotWindow::GetSlotType() const +{ + return m_dwSlotType; +} +#endif + + void CSlotWindow::SetSlotStyle(DWORD dwStyle) { m_dwSlotStyle = dwStyle; @@ -499,6 +512,7 @@ void CSlotWindow::SetSlotCountNew(DWORD dwIndex, DWORD dwGrade, DWORD dwCount) void CSlotWindow::SetSlotCoolTime(DWORD dwIndex, float fCoolTime, float fElapsedTime) { TSlot * pSlot; + if (!GetSlotPointer(dwIndex, &pSlot)) return; @@ -506,9 +520,94 @@ void CSlotWindow::SetSlotCoolTime(DWORD dwIndex, float fCoolTime, float fElapsed pSlot->fStartCoolTime = CTimer::Instance().GetCurrentSecond() - fElapsedTime; } +#ifdef FIX_REFRESH_SKILL_COOLDOWN +void CSlotWindow::StoreSlotCoolTime(DWORD dwKey, DWORD dwSlotIndex, float fCoolTime, float fElapsedTime) +{ + std::map::iterator it = m_CoolDownStore[dwKey].find(dwSlotIndex); + + if (it != m_CoolDownStore[dwKey].end()) + { + it->second.fCoolTime = fCoolTime; + it->second.fElapsedTime = CTimer::Instance().GetCurrentSecond() - fElapsedTime; + it->second.bActive = false; + } + else + { + SStoreCoolDown m_storeCoolDown; + + m_storeCoolDown.fCoolTime = fCoolTime; + m_storeCoolDown.fElapsedTime = CTimer::Instance().GetCurrentSecond() - fElapsedTime; + m_storeCoolDown.bActive = false; + + m_CoolDownStore[dwKey].insert(std::map::value_type(dwSlotIndex, m_storeCoolDown)); + } +} + +void CSlotWindow::RestoreSlotCoolTime(DWORD dwKey) +{ + for (std::map::iterator it = m_CoolDownStore[dwKey].begin(); it != m_CoolDownStore[dwKey].end(); it++) + { + TSlot* pSlot; + + if (!GetSlotPointer(it->first, &pSlot)) + return; + + pSlot->fCoolTime = it->second.fCoolTime; + pSlot->fStartCoolTime = it->second.fElapsedTime; + + if (it->second.bActive) + ActivateSlot(it->first); + else + DeactivateSlot(it->first); + } +} + +void CSlotWindow::TransferSlotCoolTime(DWORD dwIndex1, DWORD dwIndex2) +{ + std::map::iterator it = m_CoolDownStore[CPythonSkill::SKILL_TYPE_ACTIVE].find(dwIndex1); + + if (it != m_CoolDownStore[CPythonSkill::SKILL_TYPE_ACTIVE].end()) + { + TSlot* pSlot1; + + if (!GetSlotPointer(dwIndex1, &pSlot1)) + return; + + TSlot* pSlot2; + + if (!GetSlotPointer(dwIndex2, &pSlot2)) + return; + + // Replacing the cooldown from slot 1 to slot 2 and removing the slot 1 from the map. + SStoreCoolDown slotCooldown = it->second; + + int iDestSkillGrade = CPythonPlayer::Instance().GetSkillGrade(dwIndex2); + int iDestSkillLevel = CPythonPlayer::Instance().GetSkillLevel(dwIndex2); + + m_CoolDownStore[CPythonSkill::SKILL_TYPE_ACTIVE][dwIndex2] = slotCooldown; + m_CoolDownStore[CPythonSkill::SKILL_TYPE_ACTIVE].erase(dwIndex1); + + // Removing the cooldown from the slot 1. + pSlot1->fCoolTime = 0; + pSlot1->fStartCoolTime = 0; + + if (slotCooldown.bActive) + ActivateSlot(dwIndex2); + + if (iDestSkillLevel > 0) + { + // Setting the cooldown from slot 1 to slot 2. + pSlot2->fCoolTime = slotCooldown.fCoolTime; + pSlot2->fStartCoolTime = slotCooldown.fElapsedTime; + } + } +} +#endif + void CSlotWindow::ActivateSlot(DWORD dwIndex) { TSlot * pSlot; + if (!GetSlotPointer(dwIndex, &pSlot)) return; @@ -518,20 +617,36 @@ void CSlotWindow::ActivateSlot(DWORD dwIndex) { __CreateSlotEnableEffect(); } + +#ifdef FIX_REFRESH_SKILL_COOLDOWN + std::map::iterator it = m_CoolDownStore[1].find(dwIndex); + + if (it != m_CoolDownStore[1].end()) + it->second.bActive = true; +#endif } void CSlotWindow::DeactivateSlot(DWORD dwIndex) { TSlot * pSlot; + if (!GetSlotPointer(dwIndex, &pSlot)) return; pSlot->bActive = FALSE; + +#ifdef FIX_REFRESH_SKILL_COOLDOWN + std::map::iterator it = m_CoolDownStore[1].find(dwIndex); + + if (it != m_CoolDownStore[1].end()) + it->second.bActive = false; +#endif } void CSlotWindow::ClearSlot(DWORD dwIndex) { TSlot * pSlot; + if (!GetSlotPointer(dwIndex, &pSlot)) return; @@ -1262,6 +1377,17 @@ void CSlotWindow::ReserveDestroyCoolTimeFinishEffect(DWORD dwSlotIndex) m_ReserveDestroyEffectDeque.push_back(dwSlotIndex); } +#ifdef FIX_REFRESH_SKILL_COOLDOWN +void CSlotWindow::ClearStoredSlotCoolTime(DWORD dwKey, DWORD dwSlotIndex) +{ + std::map& store = m_CoolDownStore[dwKey]; + std::map::iterator it = store.find(dwSlotIndex); + + if (it != store.end()) + store.erase(it); +} +#endif + DWORD CSlotWindow::Type() { static int s_Type = GetCRC32("CSlotWindow", strlen("CSlotWindow")); @@ -1383,6 +1509,11 @@ void CSlotWindow::__Initialize() m_dwSlotStyle = SLOT_STYLE_PICK_UP; m_dwToolTipSlotNumber = SLOT_NUMBER_NONE; +#ifdef FIX_REFRESH_SKILL_COOLDOWN + m_CoolDownStore.clear(); +#endif + + m_isUseMode = FALSE; m_isUsableItem = FALSE; diff --git a/src/EterPythonLib/PythonSlotWindow.h b/src/EterPythonLib/PythonSlotWindow.h index ba45ac7..674536a 100644 --- a/src/EterPythonLib/PythonSlotWindow.h +++ b/src/EterPythonLib/PythonSlotWindow.h @@ -75,6 +75,10 @@ namespace UI } TSlot; typedef std::list TSlotList; typedef TSlotList::iterator TSlotListIterator; +#ifdef FIX_REFRESH_SKILL_COOLDOWN + typedef struct SStoreCoolDown { float fCoolTime; float fElapsedTime; bool bActive; }; +#endif + public: CSlotWindow(PyObject * ppyObject); @@ -84,6 +88,9 @@ namespace UI // Manage Slot void SetSlotType(DWORD dwType); +#ifdef FIX_REFRESH_SKILL_COOLDOWN + DWORD GetSlotType() const; +#endif void SetSlotStyle(DWORD dwStyle); void AppendSlot(DWORD dwIndex, int ixPosition, int iyPosition, int ixCellSize, int iyCellSize); @@ -107,6 +114,11 @@ namespace UI void SetSlotCount(DWORD dwIndex, DWORD dwCount); void SetSlotCountNew(DWORD dwIndex, DWORD dwGrade, DWORD dwCount); void SetSlotCoolTime(DWORD dwIndex, float fCoolTime, float fElapsedTime = 0.0f); +#ifdef FIX_REFRESH_SKILL_COOLDOWN + void StoreSlotCoolTime(DWORD dwKey, DWORD dwSlotIndex, float fCoolTime, float fElapsedTime = .0f); + void RestoreSlotCoolTime(DWORD dwKey); + void TransferSlotCoolTime(DWORD dwIndex1, DWORD dwIndex2); +#endif void ActivateSlot(DWORD dwIndex); void DeactivateSlot(DWORD dwIndex); void RefreshSlot(); @@ -150,6 +162,10 @@ namespace UI // CallBack void ReserveDestroyCoolTimeFinishEffect(DWORD dwSlotIndex); +#ifdef FIX_REFRESH_SKILL_COOLDOWN + void ClearStoredSlotCoolTime(DWORD dwKey, DWORD dwSlotIndex); +#endif + protected: void __Initialize(); void __CreateToggleSlotImage(); @@ -199,6 +215,9 @@ namespace UI std::list m_dwSelectedSlotIndexList; TSlotList m_SlotList; DWORD m_dwToolTipSlotNumber; +#ifdef FIX_REFRESH_SKILL_COOLDOWN + std::map> m_CoolDownStore; +#endif BOOL m_isUseMode; BOOL m_isUsableItem; diff --git a/src/EterPythonLib/PythonWindow.h b/src/EterPythonLib/PythonWindow.h index 6b08f0f..b58e645 100644 --- a/src/EterPythonLib/PythonWindow.h +++ b/src/EterPythonLib/PythonWindow.h @@ -81,6 +81,9 @@ namespace UI bool HasParent() { return m_pParent ? true : false; } bool HasChild() { return m_pChildList.empty() ? false : true; } int GetChildCount() { return m_pChildList.size(); } +#ifdef FIX_REFRESH_SKILL_COOLDOWN + const TWindowContainer& GetChildList() const { return m_pChildList; } +#endif CWindow * GetRoot(); CWindow * GetParent(); diff --git a/src/EterPythonLib/PythonWindowManager.cpp b/src/EterPythonLib/PythonWindowManager.cpp index 2e5c3cc..9ad6746 100644 --- a/src/EterPythonLib/PythonWindowManager.cpp +++ b/src/EterPythonLib/PythonWindowManager.cpp @@ -652,6 +652,39 @@ namespace UI m_pRightCaptureWindow = NULL; } +#ifdef FIX_REFRESH_SKILL_COOLDOWN + void CWindowManager::ClearStoredSlotCoolTimeInAllSlotWindows(DWORD dwKey, DWORD dwSlotIndex) + { + // recursively walk the window tree starting from layers and clear stored cooldown entries + std::function recurse; + + recurse = [&](CWindow* pWin) + { + if (!pWin) + return; + + // If this is a slot window, call its helper + if (pWin->IsType(UI::CSlotWindow::Type())) + { + UI::CSlotWindow * pSlotWin = static_cast(pWin); + pSlotWin->ClearStoredSlotCoolTime(dwKey, dwSlotIndex); + } + + // Visit children + for (CWindow* pChild : pWin->GetChildList()) + { + recurse(pChild); + } + }; + + // Walk all layer roots + for (CWindow* pLayer : m_LayerWindowList) + { + recurse(pLayer); + } + } +#endif + void CWindowManager::SetResolution(int hres, int vres) { if (hres<=0 || vres<=0) diff --git a/src/EterPythonLib/PythonWindowManager.h b/src/EterPythonLib/PythonWindowManager.h index d7681a2..78736f8 100644 --- a/src/EterPythonLib/PythonWindowManager.h +++ b/src/EterPythonLib/PythonWindowManager.h @@ -106,6 +106,10 @@ namespace UI void SetTop(CWindow * pWin); void SetTopUIWindow(); void ResetCapture(); +#ifdef FIX_REFRESH_SKILL_COOLDOWN + void ClearStoredSlotCoolTimeInAllSlotWindows(DWORD dwKey, DWORD dwSlotIndex); +#endif + void Update(); void Render(); diff --git a/src/EterPythonLib/PythonWindowManagerModule.cpp b/src/EterPythonLib/PythonWindowManagerModule.cpp index 9262ef9..25dd7f7 100644 --- a/src/EterPythonLib/PythonWindowManagerModule.cpp +++ b/src/EterPythonLib/PythonWindowManagerModule.cpp @@ -1254,6 +1254,80 @@ PyObject * wndMgrSetSlotCoolTime(PyObject * poSelf, PyObject * poArgs) return Py_BuildNone(); } +#ifdef FIX_REFRESH_SKILL_COOLDOWN +PyObject* wndMgrStoreSlotCoolTime(PyObject* poSelf, PyObject* poArgs) +{ + UI::CWindow* pWin; + if (!PyTuple_GetWindow(poArgs, 0, &pWin)) + return Py_BuildException(); + + int iKey; + if (!PyTuple_GetInteger(poArgs, 1, &iKey)) + return Py_BuildException(); + + int iSlotIndex; + if (!PyTuple_GetInteger(poArgs, 2, &iSlotIndex)) + return Py_BuildException(); + + float fCoolTime; + if (!PyTuple_GetFloat(poArgs, 3, &fCoolTime)) + return Py_BuildException(); + + float fElapsedTime = 0.0f; + PyTuple_GetFloat(poArgs, 4, &fElapsedTime); + + if (!pWin->IsType(UI::CSlotWindow::Type())) + return Py_BuildException(); + + UI::CSlotWindow* pSlotWin = (UI::CSlotWindow*)pWin; + pSlotWin->StoreSlotCoolTime(iKey, iSlotIndex, fCoolTime, fElapsedTime); + + return Py_BuildNone(); +} + +PyObject* wndMgrRestoreSlotCoolTime(PyObject* poSelf, PyObject* poArgs) +{ + UI::CWindow* pWin; + if (!PyTuple_GetWindow(poArgs, 0, &pWin)) + return Py_BuildException(); + + int iKey; + if (!PyTuple_GetInteger(poArgs, 1, &iKey)) + return Py_BuildException(); + + if (!pWin->IsType(UI::CSlotWindow::Type())) + return Py_BuildException(); + + UI::CSlotWindow* pSlotWin = (UI::CSlotWindow*)pWin; + pSlotWin->RestoreSlotCoolTime(iKey); + + return Py_BuildNone(); +} + +PyObject* wndMgrTransferSlotCoolTime(PyObject* poSelf, PyObject* poArgs) +{ + UI::CWindow* pWin; + if (!PyTuple_GetWindow(poArgs, 0, &pWin)) + return Py_BuildException(); + + int iIndex1; + if (!PyTuple_GetInteger(poArgs, 1, &iIndex1)) + return Py_BuildException(); + + int iIndex2; + if (!PyTuple_GetInteger(poArgs, 2, &iIndex2)) + return Py_BuildException(); + + if (!pWin->IsType(UI::CSlotWindow::Type())) + return Py_BuildException(); + + UI::CSlotWindow* pSlotWin = (UI::CSlotWindow*)pWin; + pSlotWin->TransferSlotCoolTime(iIndex1, iIndex2); + + return Py_BuildNone(); +} +#endif + PyObject * wndMgrSetToggleSlot(PyObject * poSelf, PyObject * poArgs) { assert(!"wndMgrSetToggleSlot"); @@ -2423,6 +2497,11 @@ void initwndMgr() { "SetSlotCount", wndMgrSetSlotCount, METH_VARARGS }, { "SetSlotCountNew", wndMgrSetSlotCountNew, METH_VARARGS }, { "SetSlotCoolTime", wndMgrSetSlotCoolTime, METH_VARARGS }, +#ifdef FIX_REFRESH_SKILL_COOLDOWN + { "StoreSlotCoolTime", wndMgrStoreSlotCoolTime, METH_VARARGS }, + { "RestoreSlotCoolTime", wndMgrRestoreSlotCoolTime, METH_VARARGS }, + { "TransferSlotCoolTime", wndMgrTransferSlotCoolTime, METH_VARARGS }, +#endif { "SetToggleSlot", wndMgrSetToggleSlot, METH_VARARGS }, { "ActivateSlot", wndMgrActivateSlot, METH_VARARGS }, { "DeactivateSlot", wndMgrDeactivateSlot, METH_VARARGS }, diff --git a/src/GameLib/ActorInstance.h b/src/GameLib/ActorInstance.h index b862c55..dbc8d71 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,12 @@ 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); @@ -497,6 +515,9 @@ class CActorInstance : public IActorInstance, public IFlyTargetableObject float GetHeight(); void ShowAllAttachingEffect(); void HideAllAttachingEffect(); +#ifdef __ENABLE_STEALTH_FIX__ //EXP + void HideAllAttachingEffectForEunhyeong(); +#endif void ClearAttachingEffect(); // Fishing @@ -603,6 +624,10 @@ 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); @@ -610,7 +635,11 @@ 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/GameLib/ActorInstanceAttach.cpp b/src/GameLib/ActorInstanceAttach.cpp index 2a5af52..0cfa5dc 100644 --- a/src/GameLib/ActorInstanceAttach.cpp +++ b/src/GameLib/ActorInstanceAttach.cpp @@ -580,27 +580,90 @@ void CActorInstance::ShowAllAttachingEffect() { CEffectManager::Instance().SelectEffectInstance(it->dwEffectIndex); CEffectManager::Instance().ShowEffect(); +#ifdef __ENABLE_STEALTH_FIX__ + CEffectManager::Instance().ReleaseAlwaysHidden(); + //CEffectManager::Instance().Update(); +#endif } } void CActorInstance::HideAllAttachingEffect() { std::list::iterator it; - for(it = m_AttachingEffectList.begin(); it!= m_AttachingEffectList.end();++it) + + for(it = m_AttachingEffectList.begin(); it!= m_AttachingEffectList.end(); ++it) { CEffectManager::Instance().SelectEffectInstance(it->dwEffectIndex); CEffectManager::Instance().HideEffect(); +#ifdef __ENABLE_STEALTH_FIX__ + CEffectManager::Instance().ApplyAlwaysHidden(); +#endif } } +#ifdef __ENABLE_STEALTH_FIX__ //EXP +void CActorInstance::HideAllAttachingEffectForEunhyeong() +{ + std::list::iterator it; + CEffectManager& mgr = CEffectManager::Instance(); + + for (it = m_AttachingEffectList.begin(); it != m_AttachingEffectList.end(); ++it) + { + mgr.SelectEffectInstance(it->dwEffectIndex); + DWORD crc = mgr.GetSelectedEffectDataCRC(); + CEffectData* pData = nullptr; + bool isEunh = false; + + if (crc != 0 && mgr.GetEffectData(crc, &pData) && pData) + { + std::string fname = pData->GetFileName(); + std::string fnameLower = fname; + + // normalize for case-insensitive search and path separators + for (char& c: fnameLower) + { + if (c == '\\') c = '/'; + c = (char)tolower((unsigned char)c); + } + + // Match any eunhyeongbeop variant (eunhyeongbeop, eunhyeongbeop_2, eunhyeongbeop_3, etc.) + if (fnameLower.find("eunhyeongbeop") != std::string::npos) + isEunh = true; + + Tracef("Effect Instance %u -> file=%s crc=0x%08X\n", it->dwEffectIndex, fname.c_str(), crc); + } + else + { + Tracef("Effect Instance %u -> no effect-data (crc=0x%08X)\n", it->dwEffectIndex, crc); + } + + // If this attaching effect is an eunhyeongbeop variant, do NOT hide it (show it). + if (isEunh) + { + mgr.SelectEffectInstance(it->dwEffectIndex); + mgr.ShowEffect(); + mgr.ReleaseAlwaysHidden(); + + continue; + } + + CEffectManager::Instance().SelectEffectInstance(it->dwEffectIndex); + CEffectManager::Instance().HideEffect(); + CEffectManager::Instance().ApplyAlwaysHidden(); + } +} +#endif + void CActorInstance::__ClearAttachingEffect() { m_bEffectInitialized = false; std::list::iterator it; - for(it = m_AttachingEffectList.begin(); it!= m_AttachingEffectList.end();++it) + + for(it = m_AttachingEffectList.begin(); it!= m_AttachingEffectList.end(); ++it) { CEffectManager::Instance().DestroyEffectInstance(it->dwEffectIndex); } + m_AttachingEffectList.clear(); } diff --git a/src/GameLib/ActorInstanceBattle.cpp b/src/GameLib/ActorInstanceBattle.cpp index 15d8e60..326f0ce 100644 --- a/src/GameLib/ActorInstanceBattle.cpp +++ b/src/GameLib/ActorInstanceBattle.cpp @@ -314,6 +314,76 @@ void CActorInstance::__ClearCombo() m_pkCurRaceMotionData = NULL; } +#ifdef FIX_POS_SYNC +void CActorInstance::__Push(const TPixelPosition& c_rkPPosDst, unsigned int unDuration) +{ + DWORD dwVID = GetVirtualID(); + Tracenf("VID %d SyncPixelPosition %f %f", dwVID, c_rkPPosDst.x, c_rkPPosDst.y); + + if (unDuration == 0) + unDuration = 1000; + + const D3DXVECTOR3& c_rv3Src = GetPosition(); + const D3DXVECTOR3 c_v3Delta = c_rkPPosDst - c_rv3Src; + + SetBlendingPosition(c_rkPPostDst, float(unDuration) / 1000); + + if (!IsUsingSkill() && !IsResistFallen()) + { + int len = sqrt(c_v3Delta.x * c_v3Delta.x + c_v3Delta.y * c_v3Delta.y); + if (len > 150.0f) + { + InterceptOnceMotion(CRaceMotionData::NAME_DAMAGE_FLYING); + PushOnceMotion(CRaceMotionData::NAME_STAND_UP); + } + } +} + +void CActorInstance::ClientAttack(DWORD dwVID) +{ + if (m_mapAttackSync.find(dwVID) == m_mapAttackSync.end()) { + m_mapAttackSync.insert(std::make_pair(dwVID, -1)); + } + else + { + if (m_mapAttackSync[dwVID] == 1) + { + m_mapAttackSync.erase(dwVID); + return; + } + m_mapAttackSync[dwVID]--; + } +} + +// server attack increases +void CActorInstance::ServerAttack(DWORD dwVID) +{ + if (m_mapAttackSync.find(dwVID) == m_mapAttackSync.end()) { + m_mapAttackSync.insert(std::make_pair(dwVID, 1)); + } + else + { + if (m_mapAttackSync[dwVID] == -1) + { + m_mapAttackSync.erase(dwVID); + return; + } + m_mapAttackSync[dwVID]++; + } +} + +bool CActorInstance::ProcessingClientAttack(DWORD dwVID) +{ + return m_mapAttackSync.find(dwVID) != m_mapAttackSync.end() && m_mapAttackSync[dwVID] < 0; +} + +// +bool CActorInstance::ServerAttackCameFirst(DWORD dwVID) +{ + return m_mapAttackSync.find(dwVID) != m_mapAttackSync.end() && m_mapAttackSync[dwVID] > 0; +} +#endif + //////////////////////////////////////////////////////////////////////////////////////////////////////// BOOL CActorInstance::isAttacking() @@ -610,15 +680,33 @@ void CActorInstance::__ProcessDataAttackSuccess(const NRaceData::TAttackData & c InsertDelay(c_rAttackData.fStiffenTime); +#ifdef FIX_POS_SYNC + BlendingPosition sBlending; + memset(&sBlending, 0, sizeof(sBlending)); + sBlending.source = rVictim.NEW_GetCurPixelPositionRef(); +#endif + if (__CanPushDestActor(rVictim) && c_rAttackData.fExternalForce > 0.0f) { - __PushCircle(rVictim); - - // VICTIM_COLLISION_TEST - const D3DXVECTOR3& kVictimPos = rVictim.GetPosition(); - rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce); //*nForceRatio/100.0f); +#ifdef FIX_POS_SYNC + const bool bServerAttackAlreadyCame = rVictim.ServerAttackCameFirst(GetVirtualID()); + rVictim.ClientAttack(GetVirtualID()); - // VICTIM_COLLISION_TEST_END + if (!bServerAttackAlreadyCame) + { +#endif + __PushCircle(rVictim); + + // VICTIM_COLLISION_TEST + const D3DXVECTOR3& kVictimPos = rVictim.GetPosition(); + rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce); //*nForceRatio/100.0f); + // VICTIM_COLLISION_TEST_END + +#ifdef FIX_POS_SYNC + rVictim.GetBlendingPosition(&(sBlending.dest)); + sBlending.duration = rVictim.m_PhysicsObject.GetRemainingTime(); + } +#endif } // Invisible Time @@ -690,7 +778,11 @@ void CActorInstance::__ProcessDataAttackSuccess(const NRaceData::TAttackData & c } else if (NRaceData::HIT_TYPE_GREAT == c_rAttackData.iHittingType) { +#ifdef FIX_POS_SYNC + __HitGreate(rVictim, uiSkill); +#else __HitGreate(rVictim); +#endif } else { @@ -820,10 +912,18 @@ void CActorInstance::__HitGood(CActorInstance& rVictim) } } +#ifdef FIX_POS_SYNC +void CActorInstance::__HitGreate(CActorInstance& rVictim, UINT uiSkill) +#else void CActorInstance::__HitGreate(CActorInstance& rVictim) +#endif { // DISABLE_KNOCKDOWN_ATTACK +#ifdef FIX_POS_SYNC + if (!uiSkill && rVictim.IsKnockDown()) +#else if (rVictim.IsKnockDown()) +#endif return; if (rVictim.__IsStandUpMotion()) return; @@ -907,16 +1007,24 @@ void CActorInstance::GetBlendingPosition(TPixelPosition * pPosition) { if (m_PhysicsObject.isBlending()) { +#ifdef FIX_POS_SYNC + m_PhysicsObject.GetFinalPosition(pPosition); +#else m_PhysicsObject.GetLastPosition(pPosition); pPosition->x += m_x; pPosition->y += m_y; pPosition->z += m_z; +#endif } else { +#ifdef FIX_POS_SYNC + GetPixelPosition(pPosition); +#else pPosition->x = m_x; pPosition->y = m_y; pPosition->z = m_z; +#endif } } diff --git a/src/GameLib/ItemData.h b/src/GameLib/ItemData.h index b8fbd13..6a4acf9 100644 --- a/src/GameLib/ItemData.h +++ b/src/GameLib/ItemData.h @@ -5,6 +5,7 @@ #include "EterLib/GrpSubImage.h" #include "EterGrnLib/Thing.h" +#include "GameType.h" class CItemData { diff --git a/src/GameLib/PhysicsObject.cpp b/src/GameLib/PhysicsObject.cpp index ab2fa8f..e3f1744 100644 --- a/src/GameLib/PhysicsObject.cpp +++ b/src/GameLib/PhysicsObject.cpp @@ -107,6 +107,32 @@ void CPhysicsObject::IncreaseExternalForce(const D3DXVECTOR3 & c_rvBasePosition, } } +#ifdef FIX_POS_SYNC +void CPhysicsObject::SetLastPosition(const TPixelPosition& c_rPosition, const TPixelPosition& c_rDeltaPosition, float fBlendingTime) +{ + m_v3FinalPosition.x = float(c_rPosition.x + c_rDeltaPosition.x); + m_v3FinalPosition.y = float(c_rPosition.y + c_rDeltaPosition.y); + m_v3FinalPosition.z = float(c_rPosition.z + c_rDeltaPosition.z); + m_v3DeltaPosition.x = float(c_rDeltaPosition.x); + m_v3DeltaPosition.y = float(c_rDeltaPosition.y); + m_v3DeltaPosition.z = float(c_rDeltaPosition.z); + m_xPushingPosition.Setup(0.0f, c_rDeltaPosition.x, fBlendingTime); + m_yPushingPosition.Setup(0.0f, c_rDeltaPosition.y, fBlendingTime); +} +void CPhysicsObject::GetFinalPosition(TPixelPosition* pPosition) +{ + pPosition->x = (m_v3FinalPosition.x); + pPosition->y = (m_v3FinalPosition.y); + pPosition->z = (m_v3FinalPosition.z); +} + +void CPhysicsObject::GetDeltaPosition(TPixelPosition* pPosition) +{ + pPosition->x = (m_v3DeltaPosition.x); + pPosition->y = (m_v3DeltaPosition.y); + pPosition->z = (m_v3DeltaPosition.z); +} +#else void CPhysicsObject::SetLastPosition(const TPixelPosition & c_rPosition, float fBlendingTime) { m_v3LastPosition.x = float(c_rPosition.x); @@ -115,6 +141,7 @@ void CPhysicsObject::SetLastPosition(const TPixelPosition & c_rPosition, float f m_xPushingPosition.Setup(0.0f, c_rPosition.x, fBlendingTime); m_yPushingPosition.Setup(0.0f, c_rPosition.y, fBlendingTime); } +#endif void CPhysicsObject::GetLastPosition(TPixelPosition * pPosition) { diff --git a/src/GameLib/PhysicsObject.h b/src/GameLib/PhysicsObject.h index 69c2bca..62f30fb 100644 --- a/src/GameLib/PhysicsObject.h +++ b/src/GameLib/PhysicsObject.h @@ -71,7 +71,13 @@ class CPhysicsObject void SetDirection(const D3DXVECTOR3 & c_rv3Direction); void IncreaseExternalForce(const D3DXVECTOR3 & c_rvBasePosition, float fForce); - void SetLastPosition(const TPixelPosition & c_rPosition, float fBlendingTime); +#ifdef FIX_POS_SYNC + void SetLastPosition(const TPixelPosition& c_rPosition, const TPixelPosition& c_rDeltaPosition, float fBlendingTime); + void GetFinalPosition(TPixelPosition* pPosition); + void GetDeltaPosition(TPixelPosition* pPosition); +#else + void SetLastPosition(const TPixelPosition& c_rPosition, float fBlendingTime); +#endif void GetLastPosition(TPixelPosition * pPosition); float GetXMovement(); @@ -92,6 +98,10 @@ class CPhysicsObject D3DXVECTOR3 m_v3Velocity; D3DXVECTOR3 m_v3LastPosition; +#ifdef FIX_POS_SYNC + D3DXVECTOR3 m_v3FinalPosition; + D3DXVECTOR3 m_v3DeltaPosition; +#endif CEaseOutInterpolation m_xPushingPosition; CEaseOutInterpolation m_yPushingPosition; diff --git a/src/GameLib/StdAfx.h b/src/GameLib/StdAfx.h index 3f8f1aa..5f87bdc 100644 --- a/src/GameLib/StdAfx.h +++ b/src/GameLib/StdAfx.h @@ -26,6 +26,8 @@ #include "AudioLib/StdAfx.h" #include "EffectLib/StdAfx.h" +#include "UserInterface/Locale_inc.h" + #include "GameType.h" #include "GameUtil.h" #include "MapType.h" diff --git a/src/UserInterface/AbstractPlayer.h b/src/UserInterface/AbstractPlayer.h index 9264db3..fad023e 100644 --- a/src/UserInterface/AbstractPlayer.h +++ b/src/UserInterface/AbstractPlayer.h @@ -1,6 +1,7 @@ #pragma once #include "AbstractSingleton.h" +#include "GameType.h" class CInstanceBase; diff --git a/src/UserInterface/InstanceBase.cpp b/src/UserInterface/InstanceBase.cpp index c2d9615..498da52 100644 --- a/src/UserInterface/InstanceBase.cpp +++ b/src/UserInterface/InstanceBase.cpp @@ -448,7 +448,11 @@ BOOL CInstanceBase::IsMovieMode() BOOL CInstanceBase::IsInvisibility() { +#ifdef __ENABLE_STEALTH_FIX__ + if (IsAffect(AFFECT_INVISIBILITY) || IsAffect(AFFECT_EUNHYEONG) || IsAffect(AFFECT_REVIVE_INVISIBILITY)) +#else if (IsAffect(AFFECT_INVISIBILITY)) +#endif return true; return false; @@ -1556,7 +1560,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 +1742,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 { @@ -1790,7 +1802,11 @@ void CInstanceBase::MovementProcess() m_GraphicThingInstance.SetRotation(fRotation); } +#ifdef __ENABLE_STEALTH_FIX__ + if (__IsInDustRange() && !IsAffect(AFFECT_INVISIBILITY) && !IsAffect(AFFECT_EUNHYEONG) && !IsAffect(AFFECT_REVIVE_INVISIBILITY)) +#else if (__IsInDustRange()) +#endif { float fDustDistance = NEW_GetDistanceFromDestPixelPosition(m_kPPosDust); if (IsMountingHorse()) @@ -1906,7 +1922,7 @@ void CInstanceBase::Update() } __ComboProcess(); - + ProcessDamage(); } @@ -1968,7 +1984,30 @@ void CInstanceBase::Render() ++ms_dwRenderCounter; m_kHorse.Render(); - m_GraphicThingInstance.Render(); + m_GraphicThingInstance.Render(); + +#ifdef __ENABLE_STEALTH_FIX__ + CPythonCharacterManager& rkChrMgr = CPythonCharacterManager::Instance(); + + for (auto ptr = rkChrMgr.CharacterInstanceBegin(); ptr != rkChrMgr.CharacterInstanceEnd(); ++ptr) + { + CInstanceBase* pkInstEach = *ptr; + + if (pkInstEach) + { + if (pkInstEach->IsAffect(AFFECT_INVISIBILITY) || pkInstEach->IsAffect(AFFECT_EUNHYEONG) || pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY)) + { + if (CPythonPlayer::Instance().IsMainCharacterIndex(pkInstEach->GetVirtualID())) + continue; + + if (pkInstEach->IsAffect(AFFECT_EUNHYEONG) && !pkInstEach->IsAffect(AFFECT_INVISIBILITY) && !pkInstEach->IsAffect(AFFECT_REVIVE_INVISIBILITY)) + pkInstEach->m_GraphicThingInstance.HideAllAttachingEffectForEunhyeong(); + else + pkInstEach->m_GraphicThingInstance.HideAllAttachingEffect(); + } + } + } +#endif if (CActorInstance::IsDirLine()) { diff --git a/src/UserInterface/InstanceBase.h b/src/UserInterface/InstanceBase.h index d05c52f..18de58d 100644 --- a/src/UserInterface/InstanceBase.h +++ b/src/UserInterface/InstanceBase.h @@ -3,6 +3,7 @@ #include "GameLib/RaceData.h" #include "GameLib/ActorInstance.h" +#include "StdAfx.h" #include "AffectFlagContainer.h" class CInstanceBase @@ -632,6 +633,11 @@ class CInstanceBase bool NEW_AttackToDestInstanceDirection(CInstanceBase& rkInstDst, IFlyEventHandler* pkFlyHandler); bool NEW_AttackToDestInstanceDirection(CInstanceBase& rkInstDst); +#ifdef FIX_POS_SYNC + void ServerAttack(DWORD dwVID); + bool ProcessingClientAttack(DWORD dwVID); +#endif + bool NEW_MoveToDestPixelPositionDirection(const TPixelPosition& c_rkPPosDst); void NEW_MoveToDestInstanceDirection(CInstanceBase& rkInstDst); void NEW_MoveToDirection(float fDirRot); diff --git a/src/UserInterface/InstanceBaseBattle.cpp b/src/UserInterface/InstanceBaseBattle.cpp index ba810ed..d768ab7 100644 --- a/src/UserInterface/InstanceBaseBattle.cpp +++ b/src/UserInterface/InstanceBaseBattle.cpp @@ -410,6 +410,18 @@ bool CInstanceBase::NEW_AttackToDestInstanceDirection(CInstanceBase& rkInstDst) return true; } +#ifdef FIX_POS_SYNC +void CInstanceBase::ServerAttack(DWORD dwVID) +{ + m_GraphicThingInstance.ServerAttack(dwVID); +} + +bool CInstanceBase::ProcessingClientAttack(DWORD dwVID) +{ + return m_GraphicThingInstance.ProcessingClientAttack(dwVID); +} +#endif + void CInstanceBase::AttackProcess() { if (!m_GraphicThingInstance.CanCheckAttacking()) diff --git a/src/UserInterface/InstanceBaseEffect.cpp b/src/UserInterface/InstanceBaseEffect.cpp index 888ce40..7fd7076 100644 --- a/src/UserInterface/InstanceBaseEffect.cpp +++ b/src/UserInterface/InstanceBaseEffect.cpp @@ -84,7 +84,7 @@ const D3DXCOLOR& CInstanceBase::GetIndexedNameColor(UINT eNameColor) return g_akD3DXClrName[eNameColor]; } -void CInstanceBase::AddDamageEffect(DWORD damage,BYTE flag,BOOL bSelf,BOOL bTarget) +void CInstanceBase::AddDamageEffect(DWORD damage, BYTE flag, BOOL bSelf, BOOL bTarget) { if(CPythonSystem::Instance().IsShowDamage()) { @@ -149,19 +149,26 @@ void CInstanceBase::ProcessDamage() else */ { - if(bSelf) + if (bSelf) { strDamageType = "damage_"; - if(m_bDamageEffectType==0) + + if (m_bDamageEffectType == 0) rdwCRCEft = EFFECT_DAMAGE_SELFDAMAGE; else rdwCRCEft = EFFECT_DAMAGE_SELFDAMAGE2; + m_bDamageEffectType = !m_bDamageEffectType; } - else if(bTarget == false) +#ifdef __ENABLE_STEALTH_FIX__ //EXP + else if (!bTarget || ((IsAffect(AFFECT_INVISIBILITY) || IsAffect(AFFECT_EUNHYEONG)) && bTarget)) +#else + else if (bTarget == false) +#endif { strDamageType = "nontarget_"; rdwCRCEft = EFFECT_DAMAGE_NOT_TARGET; + return;//현재 적용 안됨. } else @@ -174,34 +181,41 @@ void CInstanceBase::ProcessDamage() DWORD index = 0; DWORD num = 0; std::vector textures; - while(damage>0) + + while (damage > 0) { - if(index > 7) + if (index > 7) { TraceError("ProcessDamage무한루프 가능성"); + break; } + num = damage%10; damage /= 10; char numBuf[MAX_PATH]; - sprintf(numBuf,"%d.dds",num); - textures.push_back("d:/ymir work/effect/affect/damagevalue/"+strDamageType+numBuf); + sprintf(numBuf, "%d.dds", num); + textures.push_back("d:/ymir work/effect/affect/damagevalue/" +strDamageType + numBuf); rkEftMgr.SetEffectTextures(ms_adwCRCAffectEffect[rdwCRCEft],textures); - D3DXMATRIX matrix,matTrans; + D3DXMATRIX matrix, matTrans; D3DXMatrixIdentity(&matrix); + matrix._41 = v3Pos.x; matrix._42 = v3Pos.y; matrix._43 = v3Pos.z; - D3DXMatrixTranslation(&matrix,v3Pos.x,v3Pos.y,v3Pos.z); - D3DXMatrixMultiply(&matrix,&pCamera->GetInverseViewMatrix(),&matrix); - D3DXMatrixTranslation(&matTrans,FONT_WIDTH*index,0,0); + + D3DXMatrixTranslation(&matrix, v3Pos.x, v3Pos.y, v3Pos.z); + D3DXMatrixMultiply(&matrix, &pCamera->GetInverseViewMatrix(), &matrix); + D3DXMatrixTranslation(&matTrans, FONT_WIDTH * index, 0, 0); + matTrans._41 = -matTrans._41; matrix = matTrans*matrix; - D3DXMatrixMultiply(&matrix,&pCamera->GetViewMatrix(),&matrix); + + D3DXMatrixMultiply(&matrix, &pCamera->GetViewMatrix(), &matrix); - rkEftMgr.CreateEffect(ms_adwCRCAffectEffect[rdwCRCEft],D3DXVECTOR3(matrix._41,matrix._42,matrix._43) + rkEftMgr.CreateEffect(ms_adwCRCAffectEffect[rdwCRCEft], D3DXVECTOR3(matrix._41, matrix._42, matrix._43) ,v3Rot); textures.clear(); @@ -784,11 +798,31 @@ void CInstanceBase::__SetReviveInvisibilityAffect(bool isVisible) if (IsWearingDress()) return; - m_GraphicThingInstance.BlendAlphaValue(0.5f, 1.0f); +#ifdef __ENABLE_STEALTH_FIX__ + if (__IsMainInstance() || __MainCanSeeHiddenThing()) + { +#endif + m_GraphicThingInstance.BlendAlphaValue(0.5f, 1.0f); +#ifdef __ENABLE_STEALTH_FIX__ + } + else + { + m_GraphicThingInstance.BlendAlphaValue(0.0f, 1.0f); + m_GraphicThingInstance.HideAllAttachingEffect(); + } +#endif } else { - m_GraphicThingInstance.BlendAlphaValue(1.0f, 1.0f); +#ifdef __ENABLE_STEALTH_FIX__ + if (!IsAffect(AFFECT_EUNHYEONG) && !IsAffect(AFFECT_INVISIBILITY)) + { +#endif + m_GraphicThingInstance.BlendAlphaValue(1.0f, 1.0f); +#ifdef __ENABLE_STEALTH_FIX__ + m_GraphicThingInstance.ShowAllAttachingEffect(); + } +#endif } } @@ -808,13 +842,26 @@ void CInstanceBase::__Assassin_SetEunhyeongAffect(bool isVisible) { // 2004.10.16.myevan.은형법 완전 투명 m_GraphicThingInstance.BlendAlphaValue(0.0f, 1.0f); - m_GraphicThingInstance.HideAllAttachingEffect(); +#ifdef __ENABLE_STEALTH_FIX__ //EXP + if (!IsAffect(AFFECT_INVISIBILITY) && !IsAffect(AFFECT_REVIVE_INVISIBILITY)) + m_GraphicThingInstance.HideAllAttachingEffectForEunhyeong(); + else +#endif + m_GraphicThingInstance.HideAllAttachingEffect(); } } else { - m_GraphicThingInstance.BlendAlphaValue(1.0f, 1.0f); - m_GraphicThingInstance.ShowAllAttachingEffect(); +#ifdef __ENABLE_STEALTH_FIX__ + if (!IsAffect(AFFECT_REVIVE_INVISIBILITY) && !IsAffect(AFFECT_INVISIBILITY)) + { +#endif + m_GraphicThingInstance.BlendAlphaValue(1.0f, 1.0f); + m_GraphicThingInstance.ShowAllAttachingEffect(); +#ifdef __ENABLE_STEALTH_FIX__ + ProcessDamage(); + } +#endif } } @@ -823,8 +870,6 @@ void CInstanceBase::__Shaman_SetParalysis(bool isParalysis) m_GraphicThingInstance.SetParalysis(isParalysis); } - - void CInstanceBase::__Warrior_SetGeomgyeongAffect(bool isVisible) { if (isVisible) @@ -895,7 +940,11 @@ void CInstanceBase::__SetAffect(UINT eAffect, bool isVisible) return; break; case AFFECT_REVIVE_INVISIBILITY: +#ifdef __ENABLE_STEALTH_FIX__ + __SetReviveInvisibilityAffect(isVisible); +#else __Assassin_SetEunhyeongAffect(isVisible); +#endif break; case AFFECT_EUNHYEONG: __Assassin_SetEunhyeongAffect(isVisible); @@ -911,15 +960,28 @@ void CInstanceBase::__SetAffect(UINT eAffect, bool isVisible) // 2004.07.17.levites.isShow를 ViewFrustumCheck로 변경 if (isVisible) { +#ifndef __ENABLE_STEALTH_FIX__ m_GraphicThingInstance.ClearAttachingEffect(); __EffectContainer_Destroy(); DetachTextTail(); +#else + m_GraphicThingInstance.HideAllAttachingEffect(); +#endif } else { +#ifdef __ENABLE_STEALTH_FIX__ + if (!IsAffect(AFFECT_EUNHYEONG) && !IsAffect(AFFECT_REVIVE_INVISIBILITY)) + { + m_GraphicThingInstance.BlendAlphaValue(1.0f, 1.0f); + m_GraphicThingInstance.ShowAllAttachingEffect(); + ProcessDamage(); + } +#else m_GraphicThingInstance.BlendAlphaValue(1.0f, 1.0f); AttachTextTail(); RefreshTextTail(); +#endif } return; break; diff --git a/src/UserInterface/Locale.cpp b/src/UserInterface/Locale.cpp index b01a2ff..5e9ef9e 100644 --- a/src/UserInterface/Locale.cpp +++ b/src/UserInterface/Locale.cpp @@ -259,8 +259,8 @@ struct SLOCALEDATA const char* szSecurityKey; } gs_stLocaleData[] = { { LSS_YMIR, "ymir", 949, "testtesttesttest" }, // Korea - { LSS_EUROPE, "de", 1252, "1234abcd5678efgh" }, // GameForge (Germany) { LSS_EUROPE, "en", 1252, "1234abcd5678efgh" }, // GameForge (United Kingdom) + { LSS_EUROPE, "de", 1252, "1234abcd5678efgh" }, // GameForge (Germany) { LSS_EUROPE, "us", 1252, "1234abcd5678efgh" }, // GameForge (USA) { LSS_EUROPE, "es", 1252, "1234abcd5678efgh" }, // GameForge (Spain) { LSS_EUROPE, "it", 1252, "1234abcd5678efgh" }, // GameForge (Italy) diff --git a/src/UserInterface/Locale_inc.h b/src/UserInterface/Locale_inc.h index 66f7e71..dd635cd 100644 --- a/src/UserInterface/Locale_inc.h +++ b/src/UserInterface/Locale_inc.h @@ -6,3 +6,14 @@ #define ENABLE_DRAGON_SOUL_SYSTEM #define ENABLE_NEW_EQUIPMENT_SYSTEM //#define ENABLE_DISCORD_RPC + +// Fixes +#define FIX_MESSENGER_ACTION_SYNC +#define FIX_REFRESH_SKILL_COOLDOWN +#define FIX_SEQ_254 +#define CHAR_SELECT_STATS_IMPROVEMENT // Improve stats values in character select screen +#define __ENABLE_STEALTH_FIX__ +//#define FIX_POS_SYNC + +// Python-only +#define FIX_HORSE_SKILLS_TAB diff --git a/src/UserInterface/NetworkActorManager.cpp b/src/UserInterface/NetworkActorManager.cpp index 7b7806c..e87b27f 100644 --- a/src/UserInterface/NetworkActorManager.cpp +++ b/src/UserInterface/NetworkActorManager.cpp @@ -536,6 +536,41 @@ void CNetworkActorManager::MoveActor(const SNetworkMoveActorData& c_rkNetMoveAct rkNetActorData.m_fRot=c_rkNetMoveActorData.m_fRot; } +#ifdef FIX_POS_SYNC +void CNetworkActorManager::AttackActor(DWORD dwVID, DWORD dwAttacakerVID, LONG lDestPosX, LONG lDestPosY, const TPixelPosition& k_pSyncPos, DWORD dwBlendDuration) +{ + std::map::iterator f = m_kNetActorDict.find(dwVID); + if (m_kNetActorDict.end() == f) + { + return; + } + + SNetworkActorData& rkNetActorData = f->second; + + if (k_pSyncPos.x && k_pSyncPos.y) { + CInstanceBase* pkInstFind = __FindActor(rkNetActorData); + + if (pkInstFind) + { + const bool bProcessingClientAttack = pkInstFind->ProcessingClientAttack(dwAttacakerVID); + pkInstFind->ServerAttack(dwAttacakerVID); + + // if already blending, update + if (bProcessingClientAttack && pkInstFind->IsPushing() && pkInstFind->GetBlendingRemainTime() > 0.15) { + pkInstFind->SetBlendingPosition(k_pSyncPos, pkInstFind->GetBlendingRemainTime()); + } + else { + // otherwise sync + //pkInstFind->SCRIPT_SetPixelPosition(k_pSyncPos.x, k_pSyncPos.y); + pkInstFind->NEW_SyncPixelPosition(k_pSyncPos, dwBlendDuration); + } + } + + rkNetActorData.SetPosition(long(k_pSyncPos.x), long(k_pSyncPos.y)); + } +} +#endif + void CNetworkActorManager::SyncActor(DWORD dwVID, LONG lPosX, LONG lPosY) { std::map::iterator f=m_kNetActorDict.find(dwVID); diff --git a/src/UserInterface/NetworkActorManager.h b/src/UserInterface/NetworkActorManager.h index 577feba..4168e7c 100644 --- a/src/UserInterface/NetworkActorManager.h +++ b/src/UserInterface/NetworkActorManager.h @@ -127,6 +127,10 @@ class CNetworkActorManager : public CReferenceObject void UpdateActor(const SNetworkUpdateActorData& c_rkNetUpdateActorData); void MoveActor(const SNetworkMoveActorData& c_rkNetMoveActorData); +#ifdef FIX_POS_SYNC + void AttackActor(DWORD dwVID, DWORD dwAttacakerVID, LONG lDestPosX, LONG lDestPosY, const TPixelPosition& k_pSyncPos, DWORD dwBlendDuration); +#endif + void SyncActor(DWORD dwVID, LONG lPosX, LONG lPosY); void SetActorOwner(DWORD dwOwnerVID, DWORD dwVictimVID); diff --git a/src/UserInterface/Packet.h b/src/UserInterface/Packet.h index 3747f3d..67c2413 100644 --- a/src/UserInterface/Packet.h +++ b/src/UserInterface/Packet.h @@ -1,5 +1,10 @@ #pragma once #include "Gamelib/RaceData.h" +#include "StdAfx.h" + +#ifdef FIX_REFRESH_SKILL_COOLDOWN +#include "GameType.h" +#endif typedef uint8_t TPacketHeader; @@ -511,6 +516,18 @@ typedef struct command_attack uint32_t dwVictimVID; // 적 VID uint8_t bCRCMagicCubeProcPiece; uint8_t bCRCMagicCubeFilePiece; +#ifdef FIX_POS_SYNC + BOOL bPacket; + LONG lSX; + LONG lSY; + LONG lX; + LONG lY; + float fSyncDestX; + float fSyncDestY; + DWORD dwBlendDuration; + DWORD dwComboMotion; + DWORD dwTime; +#endif } TPacketCGAttack; typedef struct command_chat @@ -730,6 +747,9 @@ enum MESSENGER_SUBHEADER_GC_LOGOUT, MESSENGER_SUBHEADER_GC_INVITE, MESSENGER_SUBHEADER_GC_MOBILE, +#ifdef FIX_MESSENGER_ACTION_SYNC + MESSENGER_SUBHEADER_GC_REMOVE_FRIEND +#endif }; typedef struct packet_messenger @@ -1862,6 +1882,16 @@ typedef struct packet_attack uint32_t dwVID; uint32_t dwVictimVID; // 적 VID uint8_t bType; // 공격 유형 +#ifdef FIX_POS_SYNC + BOOL bPacket; + LONG lSX; + LONG lSY; + LONG lX; + LONG lY; + float fSyncDestX; + float fSyncDestY; + DWORD dwBlendDuration; +#endif } TPacketGCAttack; typedef struct packet_c2c diff --git a/src/UserInterface/PythonApplicationModule.cpp b/src/UserInterface/PythonApplicationModule.cpp index 96056dd..e80ad63 100644 --- a/src/UserInterface/PythonApplicationModule.cpp +++ b/src/UserInterface/PythonApplicationModule.cpp @@ -1305,7 +1305,6 @@ void initapp() { "OnLogoRender", appLogoRender, METH_VARARGS }, { "OnLogoOpen", appLogoOpen, METH_VARARGS }, { "OnLogoClose", appLogoClose, METH_VARARGS }, - { NULL, NULL }, }; @@ -1490,4 +1489,22 @@ void initapp() #else PyModule_AddIntConstant(poModule, "ENABLE_NEW_EQUIPMENT_SYSTEM", 0); #endif + +#ifdef FIX_MESSENGER_ACTION_SYNC + PyModule_AddIntConstant(poModule, "FIX_MESSENGER_ACTION_SYNC", 1); +#else + PyModule_AddIntConstant(poModule, "FIX_MESSENGER_ACTION_SYNC", 0); +#endif + +#ifdef FIX_REFRESH_SKILL_COOLDOWN + PyModule_AddIntConstant(poModule, "FIX_REFRESH_SKILL_COOLDOWN", 1); +#else + PyModule_AddIntConstant(poModule, "FIX_REFRESH_SKILL_COOLDOWN", 0); +#endif + +#ifdef FIX_HORSE_SKILLS_TAB + PyModule_AddIntConstant(poModule, "FIX_HORSE_SKILLS_TAB", 1); +#else + PyModule_AddIntConstant(poModule, "FIX_HORSE_SKILLS_TAB", 0); +#endif } diff --git a/src/UserInterface/PythonMessenger.cpp b/src/UserInterface/PythonMessenger.cpp index 1394d45..4dd2580 100644 --- a/src/UserInterface/PythonMessenger.cpp +++ b/src/UserInterface/PythonMessenger.cpp @@ -4,6 +4,11 @@ void CPythonMessenger::RemoveFriend(const char * c_szKey) { m_FriendNameMap.erase(c_szKey); + +#ifdef FIX_MESSENGER_ACTION_SYNC + if (m_poMessengerHandler) + PyCallClassMemberFunc(m_poMessengerHandler, "OnRemoveList", Py_BuildValue("(is)", MESSENGER_GRUOP_INDEX_FRIEND, c_szKey)); +#endif } void CPythonMessenger::OnFriendLogin(const char * c_szKey/*, const char * c_szName*/) @@ -161,6 +166,7 @@ void initMessenger() { "Destroy", messengerDestroy, METH_VARARGS }, { "RefreshGuildMember", messengerRefreshGuildMember, METH_VARARGS }, { "SetMessengerHandler", messengerSetMessengerHandler, METH_VARARGS }, + { NULL, NULL, NULL }, }; diff --git a/src/UserInterface/PythonMessenger.h b/src/UserInterface/PythonMessenger.h index 150ae9c..ac832c6 100644 --- a/src/UserInterface/PythonMessenger.h +++ b/src/UserInterface/PythonMessenger.h @@ -1,5 +1,7 @@ #pragma once +#include + class CPythonMessenger : public CSingleton { public: diff --git a/src/UserInterface/PythonNetworkStream.cpp b/src/UserInterface/PythonNetworkStream.cpp index d695c5d..88b0081 100644 --- a/src/UserInterface/PythonNetworkStream.cpp +++ b/src/UserInterface/PythonNetworkStream.cpp @@ -662,10 +662,14 @@ bool CPythonNetworkStream::RecvPingPacket() if (!Send(sizeof(TPacketCGPong), &kPacketPong)) return false; +#ifdef FIX_SEQ_254 + return SendSequence(); +#else if (IsSecurityMode()) return SendSequence(); else return true; +#endif } bool CPythonNetworkStream::RecvDefaultPacket(int header) diff --git a/src/UserInterface/PythonNetworkStream.h b/src/UserInterface/PythonNetworkStream.h index 7568c6f..66b0c09 100644 --- a/src/UserInterface/PythonNetworkStream.h +++ b/src/UserInterface/PythonNetworkStream.h @@ -8,6 +8,10 @@ #include "packet.h" +#ifdef FIX_POS_SYNC +#include +#endif + class CInstanceBase; class CNetworkActorManager; struct SNetworkActorData; @@ -136,7 +140,11 @@ class CPythonNetworkStream : public CNetworkStream, public CSingleton> RecvSkillLevel\n"); + return true; } @@ -2364,7 +2380,7 @@ bool CPythonNetworkStream::RecvSkillLevelNew() return false; } - CPythonPlayer& rkPlayer=CPythonPlayer::Instance(); + CPythonPlayer& rkPlayer = CPythonPlayer::Instance(); rkPlayer.SetSkill(7, 0); rkPlayer.SetSkill(8, 0); @@ -2412,6 +2428,31 @@ bool CPythonNetworkStream::RecvDamageInfoPacket() return true; } + +#ifdef FIX_POS_SYNC +bool CPythonNetworkStream::RecvCharacterAttackPacket() +{ + TPacketGCAttack kPacket; + + if (!Recv(sizeof(TPacketGCAttack), &kPacket)) + { + Tracen("CPythonNetworkStream::RecvCharacterAttackPacket - PACKET READ ERROR"); + return false; + } + + if (kPacket.lX && kPacket.lY) { + __GlobalPositionToLocalPosition(kPacket.lX, kPacket.lY); + } + __GlobalPositionToLocalPosition(kPacket.lSX, kPacket.lSY); + + TPixelPosition tSyncPosition = TPixelPosition{ kPacket.fSyncDestX, kPacket.fSyncDestY, 0 }; + + m_rokNetActorMgr->AttackActor(kPacket.dwVID, kPacket.dwVictimVID, kPacket.lX, kPacket.lY, tSyncPosition, kPacket.dwBlendDuration); + + return true; +} +#endif + bool CPythonNetworkStream::RecvTargetPacket() { TPacketGCTarget TargetPacket; @@ -2503,11 +2544,20 @@ bool CPythonNetworkStream::RecvChangeSpeedPacket() /////////////////////////////////////////////////////////////////////////////////////////////////// // Recv +#ifdef FIX_POS_SYNC +bool CPythonNetworkStream::SendAttackPacket(UINT uMotAttack, DWORD dwVIDVictim, BOOL bPacket, CActorInstance::BlendingPosition& sBlending) +#else bool CPythonNetworkStream::SendAttackPacket(UINT uMotAttack, DWORD dwVIDVictim) +#endif { if (!__CanActMainInstance()) return true; +#ifdef FIX_POS_SYNC + CPythonCharacterManager& rkChrMgr = CPythonCharacterManager::Instance(); + CInstanceBase* pkInstMain = rkChrMgr.GetMainInstancePtr(); +#endif + #ifdef ATTACK_TIME_LOG static DWORD prevTime = timeGetTime(); DWORD curTime = timeGetTime(); @@ -2520,6 +2570,25 @@ bool CPythonNetworkStream::SendAttackPacket(UINT uMotAttack, DWORD dwVIDVictim) kPacketAtk.header = HEADER_CG_ATTACK; kPacketAtk.bType = uMotAttack; kPacketAtk.dwVictimVID = dwVIDVictim; +#ifdef FIX_POS_SYNC + kPacketAtk.bPacket = bPacket; + kPacketAtk.lX = (long)sBlending.dest.x; + kPacketAtk.lY = (long)sBlending.dest.y; + kPacketAtk.lSX = (long)sBlending.source.x; + kPacketAtk.lSY = (long)sBlending.source.y; + kPacketAtk.fSyncDestX = sBlending.dest.x; + // sources and dest are normalized with both coordinates positive + // since fSync are ment to be broadcasted to other clients, the Y has to preserve the negative coord + kPacketAtk.fSyncDestY = -sBlending.dest.y; + kPacketAtk.dwBlendDuration = (unsigned int)(sBlending.duration * 1000); + kPacketAtk.dwComboMotion = pkInstMain->GetComboMotion(); + kPacketAtk.dwTime = ELTimer_GetServerMSec(); + + if (kPacketAtk.lX && kPacketAtk.lY) + __LocalPositionToGlobalPosition(kPacketAtk.lX, kPacketAtk.lY); + + __LocalPositionToGlobalPosition(kPacketAtk.lSX, kPacketAtk.lSY); +#endif if (!SendSpecial(sizeof(kPacketAtk), &kPacketAtk)) { @@ -2814,6 +2883,26 @@ bool CPythonNetworkStream::RecvMessenger() CPythonMessenger::Instance().SetMobile(char_name, byState); break; } + +#ifdef FIX_MESSENGER_ACTION_SYNC + case MESSENGER_SUBHEADER_GC_REMOVE_FRIEND: + { + BYTE bLength; + + if (!Recv(sizeof(bLength), &bLength)) + return false; + + if (!Recv(bLength, char_name)) + return false; + + char_name[bLength] = 0; + + CPythonMessenger::Instance().RemoveFriend(char_name); + __RefreshTargetBoardByName(char_name); + + break; + } +#endif } return true; } @@ -3984,6 +4073,7 @@ bool CPythonNetworkStream::RecvWalkModePacket() bool CPythonNetworkStream::RecvChangeSkillGroupPacket() { TPacketGCChangeSkillGroup ChangeSkillGroup; + if (!Recv(sizeof(ChangeSkillGroup), &ChangeSkillGroup)) return false; @@ -3991,6 +4081,7 @@ bool CPythonNetworkStream::RecvChangeSkillGroupPacket() CPythonPlayer::Instance().NEW_ClearSkillData(); __RefreshCharacterWindow(); + return true; } diff --git a/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp b/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp index b367ac0..ce53a7d 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp @@ -324,6 +324,10 @@ void CPythonNetworkStream::__RecvCharacterUpdatePacket(SNetworkUpdateActorData * __RefreshAlignmentWindow(); __RefreshEquipmentWindow(); __RefreshInventoryWindow(); +#ifdef CHAR_SELECT_STATS_IMPROVEMENT + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].wHairPart = pkNetUpdateActorData->m_dwHair; + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].wMainPart = pkNetUpdateActorData->m_dwArmor; +#endif } else { diff --git a/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp b/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp index b30dd35..9994e9e 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseLoading.cpp @@ -326,7 +326,21 @@ bool CPythonNetworkStream::__RecvPlayerPoints() return false; for (DWORD i = 0; i < POINT_MAX_NUM; ++i) + { CPythonPlayer::Instance().SetStatus(i, PointsPacket.points[i]); +#ifdef CHAR_SELECT_STATS_IMPROVEMENT + if (i == POINT_LEVEL) + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byLevel = PointsPacket.points[i]; + else if (i == POINT_ST) + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byST = PointsPacket.points[i]; + else if (i == POINT_HT) + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byHT = PointsPacket.points[i]; + else if (i == POINT_DX) + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byDX = PointsPacket.points[i]; + else if (i == POINT_IQ) + m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byIQ = PointsPacket.points[i]; +#endif + } PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "RefreshStatus", Py_BuildValue("()")); return true; diff --git a/src/UserInterface/PythonPlayer.cpp b/src/UserInterface/PythonPlayer.cpp index 157fb76..b8780fe 100644 --- a/src/UserInterface/PythonPlayer.cpp +++ b/src/UserInterface/PythonPlayer.cpp @@ -532,6 +532,74 @@ void CPythonPlayer::NotifyChangePKMode() PyCallClassMemberFunc(m_ppyGameWindow, "OnChangePKMode", Py_BuildValue("()")); } +#ifdef FIX_REFRESH_SKILL_COOLDOWN +void CPythonPlayer::ResetSkillCoolTimes() +{ + for (int i = 0; i < SKILL_MAX_NUM; ++i) + ResetSkillCoolTimeForSlot(i); +} + +void CPythonPlayer::ResetSkillCoolTimeForSlot(DWORD dwSlotIndex) +{ + if (dwSlotIndex >= SKILL_MAX_NUM) + return; + + TSkillInstance& rkSkillInst = m_playerStatus.aSkill[dwSlotIndex]; + + // If this skill is a toggle and currently active, deactivate it so UI/state is consistent. + // __DeactivateSkillSlot is a private/protected helper on this class. + if (IsToggleSkill(dwSlotIndex) && IsSkillActive(dwSlotIndex)) + __DeactivateSkillSlot(dwSlotIndex); + + // If nothing to clear, skip + if (!rkSkillInst.fLastUsedTime && !rkSkillInst.fCoolTime) + return; + + // Clear cooldown timers + rkSkillInst.fLastUsedTime = rkSkillInst.fCoolTime = 0.0f; + + // Get the actual skill type to clear cooldowns from the correct storage + DWORD dwSkillType = CPythonSkill::SKILL_TYPE_ACTIVE; // default + DWORD dwSkillIndex = rkSkillInst.dwIndex; + CPythonSkill::TSkillData* pSkillData = NULL; + + if (dwSkillIndex != 0 && CPythonSkill::Instance().GetSkillData(dwSkillIndex, &pSkillData)) + { + dwSkillType = pSkillData->byType; + } + + if (dwSkillType == CPythonSkill::SKILL_TYPE_ACTIVE) + { + for (int iGrade = 0; iGrade < CPythonSkill::SKILL_GRADE_COUNT; ++iGrade) + { + UI::CWindowManager::Instance().ClearStoredSlotCoolTimeInAllSlotWindows( + dwSkillType, + dwSlotIndex + iGrade * CPythonSkill::SKILL_GRADE_STEP_COUNT); + } + } + else + { + UI::CWindowManager::Instance().ClearStoredSlotCoolTimeInAllSlotWindows(dwSkillType, dwSlotIndex); + } + + // Inform Python/UI which slot was cleared + PyCallClassMemberFunc(m_ppyGameWindow, "SkillClearCoolTime", Py_BuildValue("(i)", (int)dwSlotIndex)); +} + +void CPythonPlayer::ResetHorseSkillCoolTime(DWORD dwSkillIndex, DWORD dwVisualSlotIndex) +{ + // Clear both the source slot (137-140) and the visual slot (0-3) + // so RestoreSlotCoolTime won't have anything to restore + + UI::CWindowManager::Instance().ClearStoredSlotCoolTimeInAllSlotWindows( + CPythonSkill::SKILL_TYPE_HORSE, + dwSkillIndex); + + UI::CWindowManager::Instance().ClearStoredSlotCoolTimeInAllSlotWindows( + CPythonSkill::SKILL_TYPE_HORSE, + dwVisualSlotIndex); +} +#endif void CPythonPlayer::MoveItemData(TItemPos SrcCell, TItemPos DstCell) { @@ -993,6 +1061,7 @@ void CPythonPlayer::SetSkillLevel(DWORD dwSlotIndex, DWORD dwSkillLevel) void CPythonPlayer::SetSkillLevel_(DWORD dwSkillIndex, DWORD dwSkillGrade, DWORD dwSkillLevel) { DWORD dwSlotIndex; + if (!GetSkillSlotIndex(dwSkillIndex, &dwSlotIndex)) return; @@ -1004,39 +1073,46 @@ void CPythonPlayer::SetSkillLevel_(DWORD dwSkillIndex, DWORD dwSkillGrade, DWORD case 0: m_playerStatus.aSkill[dwSlotIndex].iGrade = dwSkillGrade; m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel; + break; case 1: m_playerStatus.aSkill[dwSlotIndex].iGrade = dwSkillGrade; - m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel-20+1; + m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel - 20 + 1; + break; case 2: m_playerStatus.aSkill[dwSlotIndex].iGrade = dwSkillGrade; - m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel-30+1; + m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel - 30 + 1; + break; case 3: m_playerStatus.aSkill[dwSlotIndex].iGrade = dwSkillGrade; - m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel-40+1; + m_playerStatus.aSkill[dwSlotIndex].iLevel = dwSkillLevel - 40 + 1; + break; } const DWORD SKILL_MAX_LEVEL = 40; - - - - - if (dwSkillLevel>SKILL_MAX_LEVEL) + if (dwSkillLevel > SKILL_MAX_LEVEL) { m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = 0.0f; m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = 0.0f; TraceError("CPythonPlayer::SetSkillLevel(SlotIndex=%d, SkillLevel=%d)", dwSlotIndex, dwSkillLevel); + return; } - m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel)/100.0f; - m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel+1)/100.0f; - +#ifdef FIX_REFRESH_SKILL_COOLDOWN + if (m_playerStatus.aSkill[dwSlotIndex].iLevel <= 0) + { + ResetSkillCoolTimeForSlot(dwSlotIndex); + } +#endif + + m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel) / 100.0f; + m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel + 1) / 100.0f; } void CPythonPlayer::SetSkillCoolTime(DWORD dwSkillIndex) @@ -1568,7 +1644,19 @@ void CPythonPlayer::NEW_ClearSkillData(bool bAll) for (it = m_skillSlotDict.begin(); it != m_skillSlotDict.end();) { +#ifdef FIX_REFRESH_SKILL_COOLDOWN + CPythonSkill::TSkillData* data = nullptr; + + if (!CPythonSkill::Instance().GetSkillData(it->first, &data)) + { + ++it; + continue; + } + + if (bAll || (data->byType != CPythonSkill::SKILL_TYPE_SUPPORT && data->byType != CPythonSkill::SKILL_TYPE_HORSE && data->byType != CPythonSkill::SKILL_TYPE_GUILD)) +#else if (bAll || __GetSkillType(it->first) == CPythonSkill::SKILL_TYPE_ACTIVE) +#endif it = m_skillSlotDict.erase(it); else ++it; @@ -1576,6 +1664,24 @@ void CPythonPlayer::NEW_ClearSkillData(bool bAll) for (int i = 0; i < SKILL_MAX_NUM; ++i) { +#ifdef FIX_REFRESH_SKILL_COOLDOWN + DWORD dwSkillIndex = m_playerStatus.aSkill[i].dwIndex; + CPythonSkill::TSkillData* pSkillData = NULL; + + // Skip empty slots + if (dwSkillIndex == 0) + continue; + + if (!CPythonSkill::Instance().GetSkillData(dwSkillIndex, &pSkillData)) + continue; + + // If not clearing all, skip persistent skill types (SUPPORT, HORSE, GUILD) + if (!bAll && (pSkillData->byType == CPythonSkill::SKILL_TYPE_SUPPORT || + pSkillData->byType == CPythonSkill::SKILL_TYPE_HORSE || + pSkillData->byType == CPythonSkill::SKILL_TYPE_GUILD)) + continue; +#endif + ZeroMemory(&m_playerStatus.aSkill[i], sizeof(TSkillInstance)); } @@ -1583,8 +1689,22 @@ void CPythonPlayer::NEW_ClearSkillData(bool bAll) { // 2004.09.30.myevan.스킬갱신시 스킬 포인트업[+] 버튼이 안나와 처리 m_playerStatus.aSkill[j].iGrade = 0; - m_playerStatus.aSkill[j].fcurEfficientPercentage=0.0f; - m_playerStatus.aSkill[j].fnextEfficientPercentage=0.05f; + m_playerStatus.aSkill[j].fcurEfficientPercentage = 0.0f; + m_playerStatus.aSkill[j].fnextEfficientPercentage = 0.05f; + +#ifdef FIX_REFRESH_SKILL_COOLDOWN + m_playerStatus.aSkill[j].isCoolTime = false; + m_playerStatus.aSkill[j].fCoolTime = 0.0f; + m_playerStatus.aSkill[j].fLastUsedTime = 0.0f; + + //ResetSkillCoolTimeForSlot(j); + for (int iGrade = 0; iGrade < CPythonSkill::SKILL_GRADE_COUNT; ++iGrade) + { + UI::CWindowManager::Instance().ClearStoredSlotCoolTimeInAllSlotWindows( + CPythonSkill::SKILL_TYPE_ACTIVE, + j + iGrade * CPythonSkill::SKILL_GRADE_STEP_COUNT); + } +#endif } if (m_ppyGameWindow) diff --git a/src/UserInterface/PythonPlayer.h b/src/UserInterface/PythonPlayer.h index 51acda5..44d979e 100644 --- a/src/UserInterface/PythonPlayer.h +++ b/src/UserInterface/PythonPlayer.h @@ -262,6 +262,11 @@ class CPythonPlayer : public CSingleton, public IAbstractPlayer void NotifyCharacterUpdate(DWORD dwVID); void NotifyDeadMainCharacter(); void NotifyChangePKMode(); +#ifdef FIX_REFRESH_SKILL_COOLDOWN + void ResetSkillCoolTimes(); + void ResetSkillCoolTimeForSlot(DWORD dwSlotIndex); + void ResetHorseSkillCoolTime(DWORD dwSkillIndex, DWORD dwVisualSlotIndex); +#endif // Player Status diff --git a/src/UserInterface/PythonPlayerEventHandler.cpp b/src/UserInterface/PythonPlayerEventHandler.cpp index 676caad..067402b 100644 --- a/src/UserInterface/PythonPlayerEventHandler.cpp +++ b/src/UserInterface/PythonPlayerEventHandler.cpp @@ -132,10 +132,26 @@ void CPythonPlayerEventHandler::OnChangeShape() CPythonPlayer::Instance().NEW_Stop(); } +#ifdef FIX_POS_SYNC +void CPythonPlayerEventHandler::OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket, CActorInstance::BlendingPosition* sBlending) +#else void CPythonPlayerEventHandler::OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket) +#endif { DWORD dwVIDVictim=rkActorVictim.GetVirtualID(); +#ifdef FIX_POS_SYNC + CPythonCharacterManager::Instance().AdjustCollisionWithOtherObjects(&rkActorVictim); + CActorInstance::BlendingPosition kBlendingPacket; + memset(&kBlendingPacket, 0, sizeof(kBlendingPacket)); + + kBlendingPacket.source = rkActorVictim.NEW_GetCurPixelPositionRef(); + if (rkActorVictim.IsPushing()) { + kBlendingPacket.dest = rkActorVictim.NEW_GetLastPixelPositionRef(); + kBlendingPacket.duration = sBlending->duration; + } +#endif + // Update Target CPythonPlayer::Instance().SetTarget(dwVIDVictim, FALSE); // Update Target @@ -165,7 +181,11 @@ void CPythonPlayerEventHandler::OnHit(UINT uSkill, CActorInstance& rkActorVictim s_prevTimed[dwVIDVictim] = curTime; #endif CPythonNetworkStream& rkStream=CPythonNetworkStream::Instance(); +#ifdef FIX_POS_SYNC + rkStream.SendAttackPacket(uSkill, dwVIDVictim, isSendPacket, kBlendingPacket); +#else rkStream.SendAttackPacket(uSkill, dwVIDVictim); +#endif } if (!rkActorVictim.IsPushing()) diff --git a/src/UserInterface/PythonPlayerEventHandler.h b/src/UserInterface/PythonPlayerEventHandler.h index ca24462..ad0902a 100644 --- a/src/UserInterface/PythonPlayerEventHandler.h +++ b/src/UserInterface/PythonPlayerEventHandler.h @@ -27,7 +27,11 @@ class CPythonPlayerEventHandler : public CActorInstance::IEventHandler virtual void OnUseSkill(const SState& c_rkState, UINT uMotSkill, UINT uArg); virtual void OnUpdate(); virtual void OnChangeShape(); +#ifdef FIX_POS_SYNC + virtual void OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket, CActorInstance::BlendingPosition* sBlending); +#else virtual void OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket); +#endif void FlushVictimList(); diff --git a/src/UserInterface/PythonPlayerModule.cpp b/src/UserInterface/PythonPlayerModule.cpp index e0edc71..e2c00a9 100644 --- a/src/UserInterface/PythonPlayerModule.cpp +++ b/src/UserInterface/PythonPlayerModule.cpp @@ -543,6 +543,31 @@ PyObject * playerGetSkillCoolTime(PyObject* poSelf, PyObject* poArgs) return Py_BuildValue("ff", fCoolTime, fElapsedCoolTime); } +#ifdef FIX_REFRESH_SKILL_COOLDOWN +PyObject * playerResetSkillCoolTimeForSlot(PyObject* poSelf, PyObject* poArgs) +{ + int iSlotIndex; + if (!PyTuple_GetInteger(poArgs, 0, &iSlotIndex)) + return Py_BuildException(); + + CPythonPlayer::Instance().ResetSkillCoolTimeForSlot(iSlotIndex); + return Py_BuildNone(); +} + +PyObject* playerResetHorseSkillCoolTime(PyObject* poSelf, PyObject* poArgs) +{ + DWORD dwSkillIndex; + DWORD dwVisualSlotIndex; + + if (!PyArg_ParseTuple(poArgs, "ii", &dwSkillIndex, &dwVisualSlotIndex)) + return Py_BuildException(); + + CPythonPlayer::Instance().ResetHorseSkillCoolTime(dwSkillIndex, dwVisualSlotIndex); + + Py_RETURN_NONE; +} +#endif + PyObject * playerIsSkillActive(PyObject* poSelf, PyObject* poArgs) { int iSlotIndex; @@ -2233,6 +2258,10 @@ void initPlayer() { "IsSkillCoolTime", playerIsSkillCoolTime, METH_VARARGS }, { "GetSkillCoolTime", playerGetSkillCoolTime, METH_VARARGS }, +#ifdef FIX_REFRESH_SKILL_COOLDOWN + { "ResetSkillCoolTimeForSlot", playerResetSkillCoolTimeForSlot, METH_VARARGS }, + { "ResetHorseSkillCoolTime", playerResetHorseSkillCoolTime, METH_VARARGS }, +#endif { "IsSkillActive", playerIsSkillActive, METH_VARARGS }, { "UseGuildSkill", playerUseGuildSkill, METH_VARARGS }, { "AffectIndexToSkillIndex", playerAffectIndexToSkillIndex, METH_VARARGS },