forked from metin-server/m2dev-client-src
Merge branch 'main' into fix/client-freeze-on-drag
This commit is contained in:
@@ -2116,7 +2116,6 @@ void CInstanceBase::SetStateFlags(DWORD dwStateFlags)
|
||||
// MR-4: Fix PK Mode Bug
|
||||
// Prevent killer mode for same-guild attacks in GUILD PK mode
|
||||
bool skipKiller = false;
|
||||
|
||||
|
||||
if ((dwStateFlags & ADD_CHARACTER_STATE_KILLER) && PK_MODE_GUILD == GetPKMode()) {
|
||||
CPythonPlayer& rkPlayer = CPythonPlayer::Instance();
|
||||
@@ -2225,6 +2224,7 @@ bool CInstanceBase::IsAttackableInstance(CInstanceBase& rkInstVictim)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (PK_MODE_GUILD == GetPKMode())
|
||||
if (GetGuildID() == rkInstVictim.GetGuildID())
|
||||
return false;
|
||||
@@ -2255,6 +2255,9 @@ bool CInstanceBase::IsAttackableInstance(CInstanceBase& rkInstVictim)
|
||||
if (IsPVPInstance(rkInstVictim))
|
||||
return true;
|
||||
|
||||
if (rkInstVictim.GetPKMode() == PK_MODE_PROTECT)
|
||||
return false;
|
||||
|
||||
// MR-4: Fix PK Mode Bug
|
||||
if (PK_MODE_REVENGE == GetPKMode())
|
||||
{
|
||||
@@ -2262,12 +2265,13 @@ bool CInstanceBase::IsAttackableInstance(CInstanceBase& rkInstVictim)
|
||||
{
|
||||
if (
|
||||
(GetGuildID() == 0 || GetGuildID() != rkInstVictim.GetGuildID()) &&
|
||||
IsConflictAlignmentInstance(rkInstVictim) &&
|
||||
rkInstVictim.GetAlignment() < 0
|
||||
)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
// MR-4: -- END OF -- Fix PK Mode Bug
|
||||
}
|
||||
else
|
||||
|
||||
@@ -372,6 +372,7 @@ class CInstanceBase
|
||||
EFFECT_HAPPINESS_RING_EQUIP, // 행복의 반지 착용 순간에 발동하는 이펙트
|
||||
EFFECT_LOVE_PENDANT_EQUIP, // 행복의 반지 착용 순간에 발동하는 이펙트
|
||||
EFFECT_TEMP,
|
||||
EFFECT_AGGREGATE_MONSTER,
|
||||
EFFECT_NUM,
|
||||
};
|
||||
|
||||
|
||||
@@ -349,7 +349,12 @@ bool CInstanceBase::NEW_UseSkill(UINT uSkill, UINT uMot, UINT uMotLoopCount, boo
|
||||
float fCurRot=m_GraphicThingInstance.GetTargetRotation();
|
||||
SetAdvancingRotation(fCurRot);
|
||||
|
||||
m_GraphicThingInstance.InterceptOnceMotion(CRaceMotionData::NAME_SKILL + uMot, 0.1f, uSkill, 1.0f);
|
||||
// MR-7: Don't show skill motion if character is invisible
|
||||
if (!IsAffect(AFFECT_INVISIBILITY))
|
||||
{
|
||||
m_GraphicThingInstance.InterceptOnceMotion(CRaceMotionData::NAME_SKILL + uMot, 0.1f, uSkill, 1.0f);
|
||||
}
|
||||
// MR-7: -- END OF -- Don't show skill motion if character is invisible
|
||||
|
||||
m_GraphicThingInstance.__OnUseSkill(uMot, uMotLoopCount, isMovingSkill);
|
||||
|
||||
|
||||
@@ -1056,40 +1056,81 @@ void CInstanceBase::__DetachEffect(DWORD dwEID)
|
||||
|
||||
DWORD CInstanceBase::__AttachEffect(UINT eEftType)
|
||||
{
|
||||
// 2004.07.17.levites.isShow를 ViewFrustumCheck로 변경
|
||||
if (IsAffect(AFFECT_INVISIBILITY))
|
||||
return 0;
|
||||
|
||||
if (eEftType>=EFFECT_NUM)
|
||||
if (eEftType >= EFFECT_NUM)
|
||||
return 0;
|
||||
|
||||
if (ms_astAffectEffectAttachBone[eEftType].empty())
|
||||
{
|
||||
return m_GraphicThingInstance.AttachEffectByID(0, NULL, ms_adwCRCAffectEffect[eEftType]);
|
||||
DWORD dwEftID = m_GraphicThingInstance.AttachEffectByID(0, NULL, ms_adwCRCAffectEffect[eEftType]);
|
||||
|
||||
// MR-7: Recover affect visual effects when coming out of invisibility
|
||||
if (dwEftID && IsAffect(AFFECT_INVISIBILITY))
|
||||
{
|
||||
CEffectManager::Instance().SelectEffectInstance(dwEftID);
|
||||
CEffectManager::Instance().HideEffect();
|
||||
CEffectManager::Instance().ApplyAlwaysHidden();
|
||||
}
|
||||
|
||||
return dwEftID;
|
||||
// MR-7: -- END OF -- Recover affect visual effects when coming out of invisibility
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string & rstrBoneName = ms_astAffectEffectAttachBone[eEftType];
|
||||
const char * c_szBoneName;
|
||||
|
||||
// 양손에 붙일 때 사용한다.
|
||||
// 이런 식의 예외 처리를 해놓은 것은 캐릭터 마다 Equip 의 Bone Name 이 다르기 때문.
|
||||
if (0 == rstrBoneName.compare("PART_WEAPON"))
|
||||
{
|
||||
if (m_GraphicThingInstance.GetAttachingBoneName(CRaceData::PART_WEAPON, &c_szBoneName))
|
||||
{
|
||||
return m_GraphicThingInstance.AttachEffectByID(0, c_szBoneName, ms_adwCRCAffectEffect[eEftType]);
|
||||
// MR-7: Recover affect visual effects when coming out of invisibility
|
||||
DWORD dwEftID = m_GraphicThingInstance.AttachEffectByID(0, c_szBoneName, ms_adwCRCAffectEffect[eEftType]);
|
||||
|
||||
if (dwEftID && IsAffect(AFFECT_INVISIBILITY))
|
||||
{
|
||||
CEffectManager::Instance().SelectEffectInstance(dwEftID);
|
||||
CEffectManager::Instance().HideEffect();
|
||||
CEffectManager::Instance().ApplyAlwaysHidden();
|
||||
}
|
||||
|
||||
return dwEftID;
|
||||
// MR-7: -- END OF -- Recover affect visual effects when coming out of invisibility
|
||||
}
|
||||
}
|
||||
else if (0 == rstrBoneName.compare("PART_WEAPON_LEFT"))
|
||||
{
|
||||
if (m_GraphicThingInstance.GetAttachingBoneName(CRaceData::PART_WEAPON_LEFT, &c_szBoneName))
|
||||
{
|
||||
return m_GraphicThingInstance.AttachEffectByID(0, c_szBoneName, ms_adwCRCAffectEffect[eEftType]);
|
||||
// MR-7: Recover affect visual effects when coming out of invisibility
|
||||
DWORD dwEftID = m_GraphicThingInstance.AttachEffectByID(0, c_szBoneName, ms_adwCRCAffectEffect[eEftType]);
|
||||
|
||||
if (dwEftID && IsAffect(AFFECT_INVISIBILITY))
|
||||
{
|
||||
CEffectManager::Instance().SelectEffectInstance(dwEftID);
|
||||
CEffectManager::Instance().HideEffect();
|
||||
CEffectManager::Instance().ApplyAlwaysHidden();
|
||||
}
|
||||
|
||||
return dwEftID;
|
||||
// MR-7: -- END OF -- Recover affect visual effects when coming out of invisibility
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_GraphicThingInstance.AttachEffectByID(0, rstrBoneName.c_str(), ms_adwCRCAffectEffect[eEftType]);
|
||||
// MR-7: Recover affect visual effects when coming out of invisibility
|
||||
DWORD dwEftID = m_GraphicThingInstance.AttachEffectByID(0, rstrBoneName.c_str(), ms_adwCRCAffectEffect[eEftType]);
|
||||
|
||||
if (dwEftID && IsAffect(AFFECT_INVISIBILITY))
|
||||
{
|
||||
CEffectManager::Instance().SelectEffectInstance(dwEftID);
|
||||
CEffectManager::Instance().HideEffect();
|
||||
CEffectManager::Instance().ApplyAlwaysHidden();
|
||||
}
|
||||
|
||||
return dwEftID;
|
||||
// MR-7: -- END OF -- Recover affect visual effects when coming out of invisibility
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2260,7 +2260,8 @@ enum SPECIAL_EFFECT
|
||||
SE_EQUIP_RAMADAN_RING, // 초승달의 반지를 착용하는 순간에 발동하는 이펙트
|
||||
SE_EQUIP_HALLOWEEN_CANDY, // 할로윈 사탕을 착용(-_-;)한 순간에 발동하는 이펙트
|
||||
SE_EQUIP_HAPPINESS_RING, // 크리스마스 행복의 반지를 착용하는 순간에 발동하는 이펙트
|
||||
SE_EQUIP_LOVE_PENDANT, // 발렌타인 사랑의 팬던트(71145) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
|
||||
SE_EQUIP_LOVE_PENDANT, // 발렌타인 사랑의 팬던트(71145) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님),
|
||||
SE_AGGREGATE_MONSTER,
|
||||
};
|
||||
|
||||
typedef struct SPacketGCSpecialEffect
|
||||
|
||||
@@ -713,6 +713,12 @@ PyObject * chrmgrIsPossibleEmoticon(PyObject* poSelf, PyObject* poArgs)
|
||||
return Py_BuildValue("i", result);
|
||||
}
|
||||
|
||||
PyObject * chrmgrPreloadRaceMotions(PyObject* poSelf, PyObject* poArgs)
|
||||
{
|
||||
CRaceManager::PreloadPlayerRaceMotions();
|
||||
return Py_BuildNone();
|
||||
}
|
||||
|
||||
void initchrmgr()
|
||||
{
|
||||
static PyMethodDef s_methods[] =
|
||||
@@ -746,6 +752,7 @@ void initchrmgr()
|
||||
{ "SetAffect", chrmgrSetAffect, METH_VARARGS },
|
||||
{ "SetEmoticon", chrmgrSetEmoticon, METH_VARARGS },
|
||||
{ "IsPossibleEmoticon", chrmgrIsPossibleEmoticon, METH_VARARGS },
|
||||
{ "PreloadRaceMotions", chrmgrPreloadRaceMotions, METH_VARARGS },
|
||||
{ "RegisterEffect", chrmgrRegisterEffect, METH_VARARGS },
|
||||
{ "RegisterCacheEffect", chrmgrRegisterCacheEffect, METH_VARARGS },
|
||||
{ "RegisterPointEffect", chrmgrRegisterPointEffect, METH_VARARGS },
|
||||
@@ -841,4 +848,5 @@ void initchrmgr()
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_HAPPINESS_RING_EQUIP", CInstanceBase::EFFECT_HAPPINESS_RING_EQUIP);
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_LOVE_PENDANT_EQUIP", CInstanceBase::EFFECT_LOVE_PENDANT_EQUIP);
|
||||
|
||||
PyModule_AddIntConstant(poModule, "EFFECT_AGGREGATE_MONSTER", CInstanceBase::EFFECT_AGGREGATE_MONSTER);
|
||||
}
|
||||
|
||||
@@ -499,9 +499,27 @@ void CPythonChat::AppendChat(int iType, const char * c_szChat)
|
||||
SChatLine * pChatLine = SChatLine::New();
|
||||
pChatLine->iType = iType;
|
||||
|
||||
// Pass chat text as-is to BiDi algorithm
|
||||
// BuildVisualBidiText_Tagless will detect chat format and handle reordering
|
||||
pChatLine->Instance.SetValue(c_szChat);
|
||||
// Parse chat format "name : message" for proper BiDi handling
|
||||
// This avoids issues with usernames containing " : "
|
||||
const char* colonPos = strstr(c_szChat, " : ");
|
||||
if (colonPos && colonPos != c_szChat) // Make sure " : " is not at the start
|
||||
{
|
||||
// Extract name and message
|
||||
size_t nameLen = colonPos - c_szChat;
|
||||
const char* msgStart = colonPos + 3; // Skip " : "
|
||||
|
||||
// Create temporary buffers
|
||||
std::string name(c_szChat, nameLen);
|
||||
std::string message(msgStart);
|
||||
|
||||
// Use new SetChatValue API for proper BiDi handling
|
||||
pChatLine->Instance.SetChatValue(name.c_str(), message.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: Not in chat format (INFO, NOTICE, etc.)
|
||||
pChatLine->Instance.SetValue(c_szChat);
|
||||
}
|
||||
|
||||
if (IsRTL())
|
||||
{
|
||||
@@ -768,9 +786,23 @@ void CWhisper::AppendChat(int iType, const char * c_szChat)
|
||||
|
||||
SChatLine * pChatLine = SChatLine::New();
|
||||
|
||||
// Pass chat text as-is to BiDi algorithm
|
||||
// BuildVisualBidiText_Tagless will detect chat format and handle reordering
|
||||
pChatLine->Instance.SetValue(c_szChat);
|
||||
// Parse chat format "name : message" for proper BiDi handling
|
||||
const char* colonPos = strstr(c_szChat, " : ");
|
||||
if (colonPos && colonPos != c_szChat)
|
||||
{
|
||||
// Extract name and message
|
||||
size_t nameLen = colonPos - c_szChat;
|
||||
const char* msgStart = colonPos + 3;
|
||||
|
||||
std::string name(c_szChat, nameLen);
|
||||
std::string message(msgStart);
|
||||
|
||||
pChatLine->Instance.SetChatValue(name.c_str(), message.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
pChatLine->Instance.SetValue(c_szChat);
|
||||
}
|
||||
|
||||
if (IsRTL())
|
||||
{
|
||||
|
||||
@@ -222,7 +222,9 @@ void CPythonMiniMap::Update(float fCenterX, float fCenterY)
|
||||
}
|
||||
}
|
||||
|
||||
const float c_fMiniMapWindowRadius = 55.0f;
|
||||
// Calculate dynamic radius based on actual minimap window size
|
||||
// Subtract border width (approx 9 pixels) to keep markers inside visible area
|
||||
const float c_fMiniMapWindowRadius = (m_fWidth < m_fHeight ? m_fWidth : m_fHeight) / 2.0f - 9.0f;
|
||||
|
||||
float fDistanceFromCenterX = (rAtlasMarkInfo.m_fX - m_fCenterX) * fooCellScale * m_fScale;
|
||||
float fDistanceFromCenterY = (rAtlasMarkInfo.m_fY - m_fCenterY) * fooCellScale * m_fScale;
|
||||
@@ -230,12 +232,11 @@ void CPythonMiniMap::Update(float fCenterX, float fCenterY)
|
||||
|
||||
if (fDistanceFromCenter >= c_fMiniMapWindowRadius)
|
||||
{
|
||||
float fRadianX = acosf(fDistanceFromCenterX / fDistanceFromCenter);
|
||||
float fRadianY = asinf(fDistanceFromCenterY / fDistanceFromCenter);
|
||||
fDistanceFromCenterX = 55.0f * cosf(fRadianX);
|
||||
fDistanceFromCenterY = 55.0f * sinf(fRadianY);
|
||||
rAtlasMarkInfo.m_fMiniMapX = ( m_fWidth - (float)m_WhiteMark.GetWidth() ) / 2.0f + fDistanceFromCenterX + m_fScreenX + 2.0f;
|
||||
rAtlasMarkInfo.m_fMiniMapY = ( m_fHeight - (float)m_WhiteMark.GetHeight() ) / 2.0f + fDistanceFromCenterY + m_fScreenY + 2.0f;
|
||||
float fRadian = atan2f(fDistanceFromCenterY, fDistanceFromCenterX);
|
||||
fDistanceFromCenterX = c_fMiniMapWindowRadius * cosf(fRadian);
|
||||
fDistanceFromCenterY = c_fMiniMapWindowRadius * sinf(fRadian);
|
||||
rAtlasMarkInfo.m_fMiniMapX = ( m_fWidth - (float)m_WhiteMark.GetWidth() ) / 2.0f + fDistanceFromCenterX + m_fScreenX;
|
||||
rAtlasMarkInfo.m_fMiniMapY = ( m_fHeight - (float)m_WhiteMark.GetHeight() ) / 2.0f + fDistanceFromCenterY + m_fScreenY;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -465,7 +466,10 @@ void CPythonMiniMap::Render(float fScreenX, float fScreenY)
|
||||
if (rAtlasMarkInfo.m_fMiniMapY <= 0.0f)
|
||||
continue;
|
||||
|
||||
__RenderTargetMark(rAtlasMarkInfo.m_fMiniMapX, rAtlasMarkInfo.m_fMiniMapY);
|
||||
__RenderTargetMark(
|
||||
rAtlasMarkInfo.m_fMiniMapX + m_WhiteMark.GetWidth() / 2,
|
||||
rAtlasMarkInfo.m_fMiniMapY + m_WhiteMark.GetHeight() / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,7 +669,10 @@ bool CPythonMiniMap::Create()
|
||||
|
||||
void CPythonMiniMap::__SetPosition()
|
||||
{
|
||||
m_fMiniMapRadius = fMIN(6400.0f / ((float) CTerrainImpl::CELLSCALE) * m_fScale, 64.0f);
|
||||
// Calculate dynamic radius - use smaller dimension to ensure circular clipping
|
||||
// Subtract border width (approx 9 pixels) to keep markers inside visible area
|
||||
float fWindowRadius = (m_fWidth < m_fHeight ? m_fWidth : m_fHeight) / 2.0f - 9.0f;
|
||||
m_fMiniMapRadius = fMIN(6400.0f / ((float) CTerrainImpl::CELLSCALE) * m_fScale, fWindowRadius);
|
||||
|
||||
m_matWorld._11 = m_fWidth * m_fScale;
|
||||
m_matWorld._22 = m_fHeight * m_fScale;
|
||||
@@ -1022,11 +1029,19 @@ void CPythonMiniMap::RenderAtlas(float fScreenX, float fScreenY)
|
||||
|
||||
if (TYPE_TARGET == rAtlasMarkInfo.m_byType)
|
||||
{
|
||||
__RenderMiniWayPointMark(rAtlasMarkInfo.m_fScreenX, rAtlasMarkInfo.m_fScreenY);
|
||||
// Convert from WhiteMark-centered to actual center for rendering
|
||||
__RenderMiniWayPointMark(
|
||||
rAtlasMarkInfo.m_fScreenX + m_WhiteMark.GetWidth() / 2,
|
||||
rAtlasMarkInfo.m_fScreenY + m_WhiteMark.GetHeight() / 2
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
__RenderWayPointMark(rAtlasMarkInfo.m_fScreenX, rAtlasMarkInfo.m_fScreenY);
|
||||
// Convert from WhiteMark-centered to actual center for rendering
|
||||
__RenderWayPointMark(
|
||||
rAtlasMarkInfo.m_fScreenX + m_WhiteMark.GetWidth() / 2,
|
||||
rAtlasMarkInfo.m_fScreenY + m_WhiteMark.GetHeight() / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -849,7 +849,9 @@ bool CPythonNetworkStream::RecvSpecialEffect()
|
||||
case SE_EQUIP_LOVE_PENDANT:
|
||||
effect = CInstanceBase::EFFECT_LOVE_PENDANT_EQUIP;
|
||||
break;
|
||||
|
||||
case SE_AGGREGATE_MONSTER:
|
||||
effect = CInstanceBase::EFFECT_AGGREGATE_MONSTER;
|
||||
break;
|
||||
|
||||
default:
|
||||
TraceError("%d 는 없는 스페셜 이펙트 번호입니다.TPacketGCSpecialEffect",kSpecialEffect.type);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "MarkManager.h"
|
||||
|
||||
#include <utf8.h>
|
||||
// EPlaceDir and TextTailBiDi() template are defined in utf8.h
|
||||
|
||||
const D3DXCOLOR c_TextTail_Player_Color = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const D3DXCOLOR c_TextTail_Monster_Color = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <utf8.h>
|
||||
@@ -166,11 +168,45 @@ bool PackInitialize(const char * c_pszFolder)
|
||||
"uiloading",
|
||||
};
|
||||
|
||||
CPackManager::instance().AddPack(std::format("{}/root.pck", c_pszFolder));
|
||||
for (const std::string& packFileName : packFiles) {
|
||||
CPackManager::instance().AddPack(std::format("{}/{}.pck", c_pszFolder, packFileName));
|
||||
Tracef("PackInitialize: Loading root.pck...");
|
||||
if (!CPackManager::instance().AddPack(std::format("{}/root.pck", c_pszFolder)))
|
||||
{
|
||||
TraceError("Failed to load root.pck");
|
||||
return false;
|
||||
}
|
||||
|
||||
Tracef("PackInitialize: Loading %d pack files in parallel...", packFiles.size());
|
||||
const size_t numThreads = std::min<size_t>(std::thread::hardware_concurrency(), packFiles.size());
|
||||
const size_t packsPerThread = (packFiles.size() + numThreads - 1) / numThreads;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::atomic<size_t> failedCount(0);
|
||||
|
||||
for (size_t t = 0; t < numThreads; ++t)
|
||||
{
|
||||
threads.emplace_back([&, t]() {
|
||||
size_t start = t * packsPerThread;
|
||||
size_t end = std::min(start + packsPerThread, packFiles.size());
|
||||
|
||||
for (size_t i = start; i < end; ++i)
|
||||
{
|
||||
std::string packPath = std::format("{}/{}.pck", c_pszFolder, packFiles[i]);
|
||||
if (!CPackManager::instance().AddPack(packPath))
|
||||
{
|
||||
TraceError("Failed to load %s", packPath.c_str());
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for all threads to complete
|
||||
for (auto& thread : threads)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
|
||||
Tracef("PackInitialize: Completed! Failed: %d / %d", failedCount.load(), packFiles.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user