2V3-a: walk with the keyboard through the ported player chain

PythonPlayer, PythonPlayerInput{,Keyboard,Mouse}, PythonPlayerEventHandler,
PythonPlayerSkill, PythonPlayerModule and GameType are verbatim copies; the
`player` stub module and the pending PythonPlayer/GameType stand-ins are gone.
Unported callees (CPythonSkill, CItemData, CItemManager, CPythonItem, ...) are
pending stubs. 64-bit fixes: emotion icon handles use Py_BuildPointer, and
PyDict_Next takes Py_ssize_t.

The actor did not move because GrannyUpdateModelMatrix passed the model matrix
through. Walk/run motions use constant root-motion extraction
(AccumulationFlags=AccumulationExtracted, LoopTranslation e.g. (0,-300,0) for
the warrior run, same as the .msa Accumulation). libgr2 now reads both fields
from TrackGroups[0], and the runtime moves by LoopTranslation over each
control's local-clock span, blended by control weight. VDA extraction is not
implemented.

port.login_flow presses DIK_UP; the actor walks more than 100 cm, and the fake
server parses CG packets and sees CG_MOVE FUNC_MOVE, then FUNC_WAIT at the new
position. port.login_live against the real server walked 132 cm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-23 23:41:31 +09:00
co-authored by Claude Opus 5.5
parent 157f7c3bb4
commit ff0983d3c8
39 changed files with 10170 additions and 2039 deletions
@@ -0,0 +1,112 @@
#include "StdAfx.h"
#include "GameType.h"
std::string g_strResourcePath = "d:/ymir work/";
std::string g_strImagePath = "d:/ymir work/ui/";
std::string g_strGuildSymbolPathName = "mark/10/";
// DEFAULT_FONT
static std::string gs_strDefaultFontName = "\261\274\270\262\303\274:12.fnt";
static std::string gs_strDefaultItalicFontName = "\261\274\270\262\303\274:12i.fnt";
static CResource* gs_pkDefaultFont = NULL;
static CResource* gs_pkDefaultItalicFont = NULL;
static bool gs_isReloadDefaultFont = false;
void DefaultFont_Startup()
{
gs_pkDefaultFont = NULL;
}
void DefaultFont_Cleanup()
{
if (gs_pkDefaultFont)
gs_pkDefaultFont->Release();
}
void DefaultFont_SetName(const char * c_szFontName)
{
gs_strDefaultFontName = c_szFontName;
gs_strDefaultFontName += ".fnt";
gs_strDefaultItalicFontName = c_szFontName;
if(strchr(c_szFontName, ':'))
gs_strDefaultItalicFontName += "i";
gs_strDefaultItalicFontName += ".fnt";
gs_isReloadDefaultFont = true;
}
bool ReloadDefaultFonts()
{
CResourceManager& rkResMgr = CResourceManager::Instance();
gs_isReloadDefaultFont = false;
CResource* pkNewFont = rkResMgr.GetResourcePointer(gs_strDefaultFontName.c_str());
pkNewFont->AddReference();
if (gs_pkDefaultFont)
gs_pkDefaultFont->Release();
gs_pkDefaultFont = pkNewFont;
CResource* pkNewItalicFont = rkResMgr.GetResourcePointer(gs_strDefaultItalicFontName.c_str());
pkNewItalicFont->AddReference();
if (gs_pkDefaultItalicFont)
gs_pkDefaultItalicFont->Release();
gs_pkDefaultItalicFont = pkNewItalicFont;
return true;
}
CResource* DefaultFont_GetResource()
{
if (!gs_pkDefaultFont || gs_isReloadDefaultFont)
ReloadDefaultFonts();
return gs_pkDefaultFont;
}
CResource* DefaultItalicFont_GetResource()
{
if (!gs_pkDefaultItalicFont || gs_isReloadDefaultFont)
ReloadDefaultFonts();
return gs_pkDefaultItalicFont;
}
// END_OF_DEFAULT_FONT
void SetGuildSymbolPath(const char * c_szPathName)
{
g_strGuildSymbolPathName = "mark/";
g_strGuildSymbolPathName += c_szPathName;
g_strGuildSymbolPathName += "/";
}
const char * GetGuildSymbolFileName(DWORD dwGuildID)
{
return _getf("%s%03d.jpg", g_strGuildSymbolPathName.c_str(), dwGuildID);
}
BYTE c_aSlotTypeToInvenType[SLOT_TYPE_MAX] =
{
RESERVED_WINDOW, // SLOT_TYPE_NONE
INVENTORY, // SLOT_TYPE_INVENTORY
RESERVED_WINDOW, // SLOT_TYPE_SKILL
RESERVED_WINDOW, // SLOT_TYPE_EMOTION
RESERVED_WINDOW, // SLOT_TYPE_SHOP
RESERVED_WINDOW, // SLOT_TYPE_EXCHANGE_OWNER
RESERVED_WINDOW, // SLOT_TYPE_EXCHANGE_TARGET
RESERVED_WINDOW, // SLOT_TYPE_QUICK_SLOT
RESERVED_WINDOW, // SLOT_TYPE_SAFEBOX <- SAFEBOX, MALL의 경우 하드 코딩되어있는 LEGACY 코드를 유지함.
RESERVED_WINDOW, // SLOT_TYPE_PRIVATE_SHOP
RESERVED_WINDOW, // SLOT_TYPE_MALL <- SAFEBOX, MALL의 경우 하드 코딩되어있는 LEGACY 코드를 유지함.
DRAGON_SOUL_INVENTORY, // SLOT_TYPE_DRAGON_SOUL_INVENTORY
};
BYTE SlotTypeToInvenType(BYTE bSlotType)
{
if (bSlotType >= SLOT_TYPE_MAX)
return RESERVED_WINDOW;
else
return c_aSlotTypeToInvenType[bSlotType];
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,280 @@
#include "StdAfx.h"
#include "PythonPlayerEventHandler.h"
#include "PythonPlayer.h"
#include "PythonCharacterManager.h"
#include "PythonNetworkStream.h"
CPythonPlayerEventHandler& CPythonPlayerEventHandler::GetSingleton()
{
static CPythonPlayerEventHandler s_kPlayerEventHandler;
return s_kPlayerEventHandler;
}
CPythonPlayerEventHandler::~CPythonPlayerEventHandler()
{
}
void CPythonPlayerEventHandler::OnClearAffects()
{
CPythonPlayer::Instance().ClearAffects();
}
void CPythonPlayerEventHandler::OnSetAffect(UINT uAffect)
{
CPythonPlayer::Instance().SetAffect(uAffect);
}
void CPythonPlayerEventHandler::OnResetAffect(UINT uAffect)
{
CPythonPlayer::Instance().ResetAffect(uAffect);
}
void CPythonPlayerEventHandler::OnSyncing(const SState& c_rkState)
{
const TPixelPosition& c_rkPPosCurSyncing=c_rkState.kPPosSelf;
m_kPPosPrevWaiting=c_rkPPosCurSyncing;
}
void CPythonPlayerEventHandler::OnWaiting(const SState& c_rkState)
{
DWORD dwCurTime=ELTimer_GetMSec();
if (m_dwNextWaitingNotifyTime>dwCurTime)
return;
m_dwNextWaitingNotifyTime=dwCurTime+100;
const TPixelPosition& c_rkPPosCurWaiting=c_rkState.kPPosSelf;
float dx=m_kPPosPrevWaiting.x-c_rkPPosCurWaiting.x;
float dy=m_kPPosPrevWaiting.y-c_rkPPosCurWaiting.y;
float len=sqrt(dx*dx+dy*dy);
if (len<1.0f)
return;
m_kPPosPrevWaiting=c_rkPPosCurWaiting;
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_WAIT, 0);
//Trace("waiting\n");
}
void CPythonPlayerEventHandler::OnMoving(const SState& c_rkState)
{
DWORD dwCurTime=ELTimer_GetMSec();
if (m_dwNextMovingNotifyTime>dwCurTime)
return;
m_dwNextMovingNotifyTime=dwCurTime+300;
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_MOVE, 0);
// Trace("moving\n");
}
void CPythonPlayerEventHandler::OnMove(const SState& c_rkState)
{
DWORD dwCurTime=ELTimer_GetMSec();
m_dwNextWaitingNotifyTime=dwCurTime+100;
m_dwNextMovingNotifyTime=dwCurTime+300;
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_MOVE, 0);
// Trace("move\n");
}
void CPythonPlayerEventHandler::OnStop(const SState& c_rkState)
{
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_WAIT, 0);
// Trace("stop\n");
}
void CPythonPlayerEventHandler::OnWarp(const SState& c_rkState)
{
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_WAIT, 0);
}
void CPythonPlayerEventHandler::OnAttack(const SState& c_rkState, WORD wMotionIndex)
{
// Tracef("CPythonPlayerEventHandler::OnAttack [%d]\n", wMotionIndex);
assert(wMotionIndex < 255);
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_COMBO, wMotionIndex);
#ifdef __ATTACK_SPEED_CHECK__
static DWORD s_dwLastTime=timeGetTime();
DWORD dwCurTime=timeGetTime();
Tracef("%d\n", dwCurTime-s_dwLastTime);
s_dwLastTime=dwCurTime;
#endif
}
void CPythonPlayerEventHandler::OnUseSkill(const SState& c_rkState, UINT uMotSkill, UINT uArg)
{
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendCharacterStatePacket(c_rkState.kPPosSelf, c_rkState.fAdvRotSelf, CInstanceBase::FUNC_SKILL|uMotSkill, uArg);
}
void CPythonPlayerEventHandler::OnUpdate()
{
}
void CPythonPlayerEventHandler::OnChangeShape()
{
CPythonPlayer::Instance().NEW_Stop();
}
void CPythonPlayerEventHandler::OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket)
{
DWORD dwVIDVictim=rkActorVictim.GetVirtualID();
// Update Target
CPythonPlayer::Instance().SetTarget(dwVIDVictim, FALSE);
// Update Target
if (isSendPacket)
{
//#define ATTACK_TIME_LOG
#ifdef ATTACK_TIME_LOG
static std::map<DWORD, float> s_prevTimed;
float curTime = timeGetTime() / 1000.0f;
bool isFirst = false;
if (s_prevTimed.end() == s_prevTimed.find(dwVIDVictim))
{
s_prevTimed[dwVIDVictim] = curTime;
isFirst = true;
}
float diffTime = curTime-s_prevTimed[dwVIDVictim];
if (diffTime < 0.1f && !isFirst)
{
TraceError("ATTACK(SPEED_HACK): %.4f(%.4f) %d", curTime, diffTime, dwVIDVictim);
}
else
{
TraceError("ATTACK: %.4f(%.4f) %d", curTime, diffTime, dwVIDVictim);
}
s_prevTimed[dwVIDVictim] = curTime;
#endif
CPythonNetworkStream& rkStream=CPythonNetworkStream::Instance();
rkStream.SendAttackPacket(uSkill, dwVIDVictim);
}
if (!rkActorVictim.IsPushing())
return;
// 거대 몬스터 밀림 제외
extern bool IS_HUGE_RACE(unsigned int vnum);
if (IS_HUGE_RACE(rkActorVictim.GetRace()))
return;
CPythonCharacterManager::Instance().AdjustCollisionWithOtherObjects(&rkActorVictim);
const TPixelPosition& kPPosLast=rkActorVictim.NEW_GetLastPixelPositionRef();
SVictim kVictim;
kVictim.m_dwVID=dwVIDVictim;
kVictim.m_lPixelX=long(kPPosLast.x);
kVictim.m_lPixelY=long(kPPosLast.y);
rkActorVictim.TEMP_Push(kVictim.m_lPixelX, kVictim.m_lPixelY);
m_kVctkVictim.push_back(kVictim);
}
void CPythonPlayerEventHandler::FlushVictimList()
{
if (m_kVctkVictim.empty())
return;
// #0000682: [M2EU] 대진각 스킬 사용시 튕김
unsigned int SYNC_POSITION_COUNT_LIMIT = 16;
unsigned int uiVictimCount = m_kVctkVictim.size();
CPythonNetworkStream& rkStream=CPythonNetworkStream::Instance();
TPacketCGSyncPosition kPacketSyncPos;
kPacketSyncPos.bHeader=HEADER_CG_SYNC_POSITION;
kPacketSyncPos.wSize=sizeof(kPacketSyncPos)+sizeof(TPacketCGSyncPositionElement) * uiVictimCount;
rkStream.Send(sizeof(kPacketSyncPos), &kPacketSyncPos);
for (unsigned int i = 0; i < uiVictimCount; ++i)
{
const SVictim& rkVictim = m_kVctkVictim[i];
rkStream.SendSyncPositionElementPacket(rkVictim.m_dwVID, rkVictim.m_lPixelX, rkVictim.m_lPixelY);
}
rkStream.SendSequence();
m_kVctkVictim.clear();
}
CPythonPlayerEventHandler::CPythonPlayerEventHandler()
{
m_dwPrevComboIndex=0;
m_dwNextMovingNotifyTime=0;
m_dwNextWaitingNotifyTime=0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
void CPythonPlayerEventHandler::CNormalBowAttack_FlyEventHandler_AutoClear::OnSetFlyTarget()
{
SState s;
m_pInstMain->NEW_GetPixelPosition(&s.kPPosSelf);
s.fAdvRotSelf=m_pInstMain->GetGraphicThingInstancePtr()->GetTargetRotation();
CPythonNetworkStream& rpns=CPythonNetworkStream::Instance();
rpns.SendFlyTargetingPacket(m_pInstTarget->GetVirtualID(), m_pInstTarget->GetGraphicThingInstancePtr()->OnGetFlyTargetPosition());
}
void CPythonPlayerEventHandler::CNormalBowAttack_FlyEventHandler_AutoClear::OnShoot(DWORD dwSkillIndex)
{
CPythonNetworkStream& rpns=CPythonNetworkStream::Instance();
rpns.SendShootPacket(dwSkillIndex);
}
void CPythonPlayerEventHandler::CNormalBowAttack_FlyEventHandler_AutoClear::Set(CPythonPlayerEventHandler * pParent, CInstanceBase * pInstMain, CInstanceBase * pInstTarget)
{
m_pParent=(pParent);
m_pInstMain=(pInstMain);
m_pInstTarget=(pInstTarget);
}
void CPythonPlayerEventHandler::CNormalBowAttack_FlyEventHandler_AutoClear::SetTarget(CInstanceBase* pInstTarget)
{
m_pInstTarget = pInstTarget;
}
void CPythonPlayerEventHandler::CNormalBowAttack_FlyEventHandler_AutoClear::OnExplodingAtAnotherTarget(DWORD dwSkillIndex, DWORD dwVID)
{
return;
Tracef("Shoot : \264\331\270\245 target\277\241 \270\302\276\322\275\300\264\317\264\331 : %d, %d\n", dwSkillIndex, dwVID);
CPythonNetworkStream& rkStream=CPythonNetworkStream::Instance();
rkStream.SendAttackPacket(dwSkillIndex, dwVID);
CPythonCharacterManager& rkChrMgr = CPythonCharacterManager::Instance();
CInstanceBase * pInstance = rkChrMgr.GetInstancePtr(dwVID);
if (pInstance)
{
pInstance->GetGraphicThingInstanceRef().OnShootDamage();
}
}
void CPythonPlayerEventHandler::CNormalBowAttack_FlyEventHandler_AutoClear::OnExplodingAtTarget(DWORD dwSkillIndex)
{
// Tracef("Shoot : 원하는 target에 맞았습니다 : %d, %d\n", dwSkillIndex, m_pInstTarget->GetVirtualID());
// CPythonNetworkStream& rkStream=CPythonNetworkStream::Instance();
// rkStream.SendAttackPacket(dwSkillIndex, m_pInstTarget->GetVirtualID());
}
@@ -0,0 +1,89 @@
#pragma once
#include "../GameLib/ActorInstance.h"
#include "../GameLib/FlyHandler.h"
#include "PythonNetworkStream.h"
#include "InstanceBase.h"
class CPythonPlayerEventHandler : public CActorInstance::IEventHandler
{
public:
static CPythonPlayerEventHandler& GetSingleton();
public:
virtual ~CPythonPlayerEventHandler();
virtual void OnSyncing(const SState& c_rkState);
virtual void OnWaiting(const SState& c_rkState);
virtual void OnMoving(const SState& c_rkState);
virtual void OnMove(const SState& c_rkState);
virtual void OnStop(const SState& c_rkState);
virtual void OnWarp(const SState& c_rkState);
virtual void OnClearAffects();
virtual void OnSetAffect(UINT uAffect);
virtual void OnResetAffect(UINT uAffect);
virtual void OnAttack(const SState& c_rkState, WORD wMotionIndex);
virtual void OnUseSkill(const SState& c_rkState, UINT uMotSkill, UINT uArg);
virtual void OnUpdate();
virtual void OnChangeShape();
virtual void OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket);
void FlushVictimList();
protected:
CPythonPlayerEventHandler();
protected:
struct SVictim
{
DWORD m_dwVID;
long m_lPixelX;
long m_lPixelY;
};
protected:
std::vector<SVictim> m_kVctkVictim;
DWORD m_dwPrevComboIndex;
DWORD m_dwNextWaitingNotifyTime;
DWORD m_dwNextMovingNotifyTime;
TPixelPosition m_kPPosPrevWaiting;
private:
class CNormalBowAttack_FlyEventHandler_AutoClear : public IFlyEventHandler
{
public:
CNormalBowAttack_FlyEventHandler_AutoClear() {}
virtual ~CNormalBowAttack_FlyEventHandler_AutoClear() {}
void Set(CPythonPlayerEventHandler * pParent, CInstanceBase * pInstMain, CInstanceBase * pInstTarget);
void SetTarget(CInstanceBase* pInstTarget);
virtual void OnSetFlyTarget();
virtual void OnShoot(DWORD dwSkillIndex);
virtual void OnNoTarget() { /*Tracenf("Shoot : target이 없습니다.");*/ }
virtual void OnExplodingOutOfRange() { /*Tracenf("Shoot : 사정거리가 끝났습니다.");*/ }
virtual void OnExplodingAtBackground() { /*Tracenf("Shoot : 배경에 맞았습니다.");*/ }
virtual void OnExplodingAtAnotherTarget(DWORD dwSkillIndex, DWORD dwVID);
virtual void OnExplodingAtTarget(DWORD dwSkillIndex);
protected:
CPythonPlayerEventHandler * m_pParent;
CInstanceBase * m_pInstMain;
CInstanceBase * m_pInstTarget;
} m_NormalBowAttack_FlyEventHandler_AutoClear;
public:
IFlyEventHandler * GetNormalBowAttackFlyEventHandler(CInstanceBase* pInstMain, CInstanceBase* pInstTarget)
{
m_NormalBowAttack_FlyEventHandler_AutoClear.Set(this,pInstMain,pInstTarget);
return &m_NormalBowAttack_FlyEventHandler_AutoClear;
}
void ChangeFlyTarget(CInstanceBase* pInstTarget)
{
m_NormalBowAttack_FlyEventHandler_AutoClear.SetTarget(pInstTarget);
}
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,134 @@
#include "StdAfx.h"
#include "PythonPlayer.h"
#include "InstanceBase.h"
void CPythonPlayer::SetAttackKeyState(bool isPress)
{
if (isPress)
{
CInstanceBase* pkInstMain = NEW_GetMainActorPtr();
if (pkInstMain)
if (pkInstMain->IsFishingMode())
{
NEW_Fishing();
return;
}
}
m_isAtkKey=isPress;
}
void CPythonPlayer::NEW_SetSingleDIKKeyState(int eDIKKey, bool isPress)
{
if (NEW_CancelFishing())
return;
switch (eDIKKey)
{
case DIK_UP:
NEW_SetSingleDirKeyState(DIR_UP, isPress);
break;
case DIK_DOWN:
NEW_SetSingleDirKeyState(DIR_DOWN, isPress);
break;
case DIK_LEFT:
NEW_SetSingleDirKeyState(DIR_LEFT, isPress);
break;
case DIK_RIGHT:
NEW_SetSingleDirKeyState(DIR_RIGHT, isPress);
break;
}
}
void CPythonPlayer::NEW_SetSingleDirKeyState(int eDirKey, bool isPress)
{
switch (eDirKey)
{
case DIR_UP:
m_isUp=isPress;
break;
case DIR_DOWN:
m_isDown=isPress;
break;
case DIR_LEFT:
m_isLeft=isPress;
break;
case DIR_RIGHT:
m_isRight=isPress;
break;
}
m_isDirKey=(m_isUp || m_isDown || m_isLeft || m_isRight);
NEW_SetMultiDirKeyState(m_isLeft, m_isRight, m_isUp, m_isDown);
}
void CPythonPlayer::NEW_SetMultiDirKeyState(bool isLeft, bool isRight, bool isUp, bool isDown)
{
if (!__CanMove())
return;
bool isAny=(isLeft || isRight || isUp || isDown);
if (isAny)
{
float fDirRot=0.0f;
NEW_GetMultiKeyDirRotation(isLeft, isRight, isUp, isDown, &fDirRot);
if (!NEW_MoveToDirection(fDirRot))
{
Tracen("CPythonPlayer::NEW_SetMultiKeyState - NEW_Move -> ERROR");
return;
}
}
else
{
NEW_Stop();
}
}
float CPythonPlayer::GetDegreeFromDirection(int iUD, int iLR)
{
switch(iUD)
{
case KEYBOARD_UD_UP:
if (KEYBOARD_LR_LEFT == iLR)
{
return +45.0f;
}
else if (KEYBOARD_LR_RIGHT == iLR)
{
return -45.0f;
}
return 0.0f;
break;
case KEYBOARD_UD_DOWN:
if (KEYBOARD_LR_LEFT == iLR)
{
return +135.0f;
}
else if (KEYBOARD_LR_RIGHT == iLR)
{
return -135.0f;
}
return +180.0f;
break;
case KEYBOARD_UD_NONE:
if (KEYBOARD_LR_LEFT == iLR)
{
return +90.0f;
}
else if (KEYBOARD_LR_RIGHT == iLR)
{
return -90.0f;
}
break;
}
return 0.0f;
}
@@ -0,0 +1,394 @@
#include "StdAfx.h"
#include "PythonPlayer.h"
#include "PythonApplication.h"
#include "../EterLib/Camera.h"
void CPythonPlayer::NEW_SetMouseMoveState(int eMBS)
{
if (MBS_PRESS==eMBS)
{
NEW_MoveToMouseScreenDirection();
m_isDirMov=true;
}
else if (MBS_CLICK==eMBS)
{
NEW_Stop();
m_isDirMov=false;
}
}
bool CPythonPlayer::NEW_MoveToMouseScreenDirection()
{
UI::CWindowManager& rkWndMgr=UI::CWindowManager::Instance();
long lMouseX;
long lMouseY;
rkWndMgr.GetMousePosition(lMouseX, lMouseY);
long lScrWidth = rkWndMgr.GetScreenWidth();
long lScrHeight = rkWndMgr.GetScreenHeight();
float fMouseX=lMouseX/float(lScrWidth);
float fMouseY=lMouseY/float(lScrHeight);
float fDirRot;
NEW_GetMouseDirRotation(fMouseX, fMouseY, &fDirRot);
return NEW_MoveToDirection(fDirRot);
}
void CPythonPlayer::NEW_SetMouseCameraState(int eMBS)
{
CPythonApplication & rkApp = CPythonApplication::Instance();
CPythonBackground & rkBG = CPythonBackground::Instance();
CCamera* pkCmrCur=CCameraManager::Instance().GetCurrentCamera();
if (pkCmrCur)
{
if (MBS_PRESS==eMBS)
{
UI::CWindowManager& rkWndMgr=UI::CWindowManager::Instance();
long lMouseX;
long lMouseY;
rkWndMgr.GetMousePosition(lMouseX, lMouseY);
pkCmrCur->BeginDrag(lMouseX, lMouseY);
if ( !rkBG.IsMapReady() )
return;
rkApp.SetCursorNum(CPythonApplication::CAMERA_ROTATE);
if ( CPythonApplication::CURSOR_MODE_HARDWARE == rkApp.GetCursorMode())
rkApp.SetCursorVisible(FALSE, true);
}
else if (MBS_CLICK==eMBS)
{
bool isCameraDrag=pkCmrCur->EndDrag();
if ( !rkBG.IsMapReady() )
return;
rkApp.SetCursorNum(CPythonApplication::NORMAL);
if ( CPythonApplication::CURSOR_MODE_HARDWARE == rkApp.GetCursorMode())
rkApp.SetCursorVisible(TRUE);
if (!isCameraDrag)
{
__ChangeTargetToPickedInstance();
CInstanceBase * pkInstPicked;
if (__GetPickedActorPtr(&pkInstPicked))
{
OpenCharacterMenu(pkInstPicked->GetVirtualID());
}
}
}
}
}
void CPythonPlayer::NEW_SetMouseSmartState(int eMBS, bool isAuto)
{
CInstanceBase* pkInstMain=NEW_GetMainActorPtr();
if (!pkInstMain) return;
// PrivateShop
if (IsOpenPrivateShop())
{
// ShopPacket 이 오기전 잠깐 동안
m_isSmtMov=false;
return;
}
// Emotion
if (__IsProcessingEmotion())
{
return;
}
// AFFECT_STUN 처리
if (pkInstMain->IsSleep())
{
return;
}
if (MBS_PRESS==eMBS)
{
m_isSmtMov=true;
__OnPressSmart(*pkInstMain, isAuto);
}
else if (MBS_CLICK==eMBS)
{
m_isSmtMov=false;
__OnClickSmart(*pkInstMain, isAuto);
}
}
void CPythonPlayer::__OnPressSmart(CInstanceBase& rkInstMain, bool isAuto)
{
DWORD dwPickedItemID;
DWORD dwPickedActorID;
TPixelPosition kPPosPickedGround;
bool isPickedItemID=__GetPickedItemID(&dwPickedItemID);
bool isPickedActorID=__GetPickedActorID(&dwPickedActorID);
bool isPickedGroundPos=__GetPickedGroundPos(&kPPosPickedGround);
if (isPickedItemID)
{
__OnPressItem(rkInstMain, dwPickedItemID);
}
else if (isPickedActorID && dwPickedActorID != GetMainCharacterIndex())
{
__OnPressActor(rkInstMain, dwPickedActorID, isAuto);
}
else if (isPickedGroundPos)
{
__OnPressGround(rkInstMain, kPPosPickedGround);
}
else
{
__OnPressScreen(rkInstMain);
}
}
void CPythonPlayer::__OnClickSmart(CInstanceBase& rkInstMain, bool isAuto)
{
DWORD dwPickedItemID;
DWORD dwPickedActorID;
TPixelPosition kPPosPickedGround;
if (__GetPickedItemID(&dwPickedItemID))
{
__OnClickItem(rkInstMain, dwPickedItemID);
}
else if (__GetPickedActorID(&dwPickedActorID))
{
__OnClickActor(rkInstMain, dwPickedActorID, isAuto);
}
else if (__GetPickedGroundPos(&kPPosPickedGround))
{
__OnClickGround(rkInstMain, kPPosPickedGround);
}
else
{
rkInstMain.NEW_Stop();
}
}
void CPythonPlayer::__ShowPickedEffect(const TPixelPosition& c_rkPPosPickedGround)
{
#ifdef __MOVIE_MODE__
CInstanceBase* pkInstMain=NEW_GetMainActorPtr();
if (!pkInstMain) return;
if (pkInstMain->IsMovieMode())
return;
#endif
NEW_ShowEffect(EFFECT_PICK, c_rkPPosPickedGround);
}
bool CPythonPlayer::NEW_SetMouseFunc(int eMBT, int eMBF)
{
if (eMBT>=MBT_NUM)
return false;
m_aeMBFButton[eMBT]=eMBF;
return true;
}
int CPythonPlayer::NEW_GetMouseFunc(int eMBT)
{
if (eMBT>=MBT_NUM)
return false;
return m_aeMBFButton[eMBT];
}
void CPythonPlayer::SetQuickCameraMode(bool isEnable)
{
if (isEnable)
{
}
else
{
NEW_SetMouseCameraState(MBS_CLICK);
}
}
bool CPythonPlayer::NEW_SetMouseState(int eMBT, int eMBS)
{
if (eMBT>=MBT_NUM)
return false;
int eMBF=m_aeMBFButton[eMBT];
switch (eMBF)
{
case MBF_MOVE:
if (__CanMove())
NEW_SetMouseMoveState(eMBS);
break;
case MBF_SMART:
if (CPythonApplication::Instance().IsPressed(DIK_LCONTROL) || CPythonApplication::Instance().IsPressed(DIK_RCONTROL))
{
NEW_Attack();
}
else
{
NEW_SetMouseSmartState(eMBS, false);
}
break;
case MBF_CAMERA:
NEW_SetMouseCameraState(eMBS);
break;
case MBF_AUTO:
NEW_SetMouseSmartState(eMBS, true);
break;
case MBF_ATTACK:
NEW_Attack();
break;
case MBF_SKILL:
if (CPythonApplication::Instance().IsPressed(DIK_LCONTROL))
{
NEW_SetMouseCameraState(eMBS);
}
else
{
if (MBS_PRESS == eMBS)
{
__ChangeTargetToPickedInstance();
__UseCurrentSkill();
}
}
break;
}
return true;
}
void CPythonPlayer::NEW_SetMouseMiddleButtonState(int eMBState)
{
NEW_SetMouseCameraState(eMBState);
}
void CPythonPlayer::NEW_RefreshMouseWalkingDirection()
{
CInstanceBase* pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain) return;
switch (m_eReservedMode)
{
case MODE_CLICK_ITEM:
{
CPythonItem& rkIT=CPythonItem::Instance();
TPixelPosition kPPosPickedItem;
if (rkIT.GetGroundItemPosition(m_dwIIDReserved, &kPPosPickedItem))
{
if (pkInstMain->NEW_GetDistanceFromDestPixelPosition(kPPosPickedItem)<20.0f)
{
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
TPixelPosition kPPosCur;
pkInstMain->NEW_GetPixelPosition(&kPPosCur);
float fCurRot=pkInstMain->GetRotation();
rkNetStream.SendCharacterStatePacket(kPPosCur, fCurRot, CInstanceBase::FUNC_WAIT, 0);
SendClickItemPacket(m_dwIIDReserved);
pkInstMain->NEW_Stop();
__ClearReservedAction();
}
else
{
pkInstMain->NEW_MoveToDestPixelPositionDirection(kPPosPickedItem);
}
}
else
{
__ClearReservedAction();
}
break;
}
case MODE_CLICK_ACTOR:
{
__ReserveProcess_ClickActor();
break;
}
case MODE_CLICK_POSITION:
{
if (!pkInstMain->isLock())
if (NEW_IsEmptyReservedDelayTime(CPythonApplication::Instance().GetGlobalElapsedTime()))
{
pkInstMain->NEW_MoveToDestPixelPositionDirection(m_kPPosReserved);
__ClearReservedAction();
}
break;
}
case MODE_USE_SKILL:
{
CInstanceBase* pkInstReserved=NEW_FindActorPtr(m_dwVIDReserved);
if (pkInstReserved)
{
float fDistance=pkInstMain->GetDistance(pkInstReserved);
// #0000806: [M2EU] 수룡에게 무사(나한군) 탄환격 스킬 사용 안됨
extern bool IS_HUGE_RACE(unsigned int vnum);
if (IS_HUGE_RACE(pkInstReserved->GetRace())) // 거대 종족은 근접을 못함
{
fDistance -= 200.0f; // TEMP: 일단 하드 코딩 처리. 정석적으로는 바운드 스피어를 고려해야함
}
if (fDistance < float(m_dwSkillRangeReserved))
{
// 접근 도중 Target 이 바뀌어 있을 수도 있다 - [levites]
SetTarget(m_dwVIDReserved);
if (__UseSkill(m_dwSkillSlotIndexReserved))
__ClearReservedAction();
}
else
{
pkInstMain->NEW_MoveToDestInstanceDirection(*pkInstReserved);
}
}
else
{
__ClearReservedAction();
}
break;
}
}
if (m_isSmtMov)
__OnPressSmart(*pkInstMain, false);
if (m_isDirMov)
NEW_MoveToMouseScreenDirection();
if (m_isDirKey)
NEW_SetMultiDirKeyState(m_isLeft, m_isRight, m_isUp, m_isDown);
if (m_isAtkKey)
NEW_Attack();
m_iComboOld=pkInstMain->GetComboIndex();
}
BOOL CPythonPlayer::__IsRightButtonSkillMode()
{
return MBF_SKILL == m_aeMBFButton[MBT_RIGHT];
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,991 @@
#include "StdAfx.h"
#include "PythonPlayer.h"
#include "PythonTextTail.h"
#include "PythonNetworkStream.h"
#include "InstanceBase.h"
#include "PythonChat.h"
#include "PythonGuild.h"
#include "../GameLib/ItemManager.h"
void CPythonPlayer::ClearAffects()
{
PyCallClassMemberFunc(m_ppyGameWindow, "ClearAffects", Py_BuildValue("()"));
}
void CPythonPlayer::SetAffect(UINT uAffect)
{
PyCallClassMemberFunc(m_ppyGameWindow, "SetAffect", Py_BuildValue("(i)", uAffect));
/////
DWORD dwSkillIndex;
if (!AffectIndexToSkillIndex(uAffect, &dwSkillIndex))
return;
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(dwSkillIndex, &pSkillData))
return;
if (!pSkillData->IsToggleSkill())
return;
DWORD dwSkillSlotIndex;
if (!GetSkillSlotIndex(dwSkillIndex, &dwSkillSlotIndex))
return;
__ActivateSkillSlot(dwSkillSlotIndex);
}
void CPythonPlayer::ResetAffect(UINT uAffect)
{
// 2004.07.17.myevan.스킬 아닌 이펙트가 안 사라지는 문제
PyCallClassMemberFunc(m_ppyGameWindow, "ResetAffect", Py_BuildValue("(i)", uAffect));
DWORD dwSkillIndex;
if (!AffectIndexToSkillIndex(uAffect, &dwSkillIndex))
return;
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(dwSkillIndex, &pSkillData))
return;
if (!pSkillData->IsToggleSkill())
return;
DWORD dwSkillSlotIndex;
if (!GetSkillSlotIndex(dwSkillIndex, &dwSkillSlotIndex))
return;
__DeactivateSkillSlot(dwSkillSlotIndex);
}
bool CPythonPlayer::FindSkillSlotIndexBySkillIndex(DWORD dwSkillIndex, DWORD * pdwSkillSlotIndex)
{
for (int i = 0; i < SKILL_MAX_NUM; ++i)
{
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[i];
if (dwSkillIndex == rkSkillInst.dwIndex)
{
*pdwSkillSlotIndex = i;
return true;
}
}
return false;
}
void CPythonPlayer::ChangeCurrentSkillNumberOnly(DWORD dwSlotIndex)
{
if (dwSlotIndex >= SKILL_MAX_NUM)
return;
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSlotIndex];
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(rkSkillInst.dwIndex, &pSkillData))
return;
if (!pSkillData->IsCanUseSkill())
return;
if (!__IsRightButtonSkillMode())
{
if (!__IsTarget())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NEED_TARGET"));
return;
}
ClickSkillSlot(dwSlotIndex);
}
else
{
m_dwcurSkillSlotIndex = dwSlotIndex;
PyCallClassMemberFunc(m_ppyGameWindow, "ChangeCurrentSkill", Py_BuildValue("(i)", dwSlotIndex));
}
}
void CPythonPlayer::ClickSkillSlot(DWORD dwSlotIndex)
{
if (dwSlotIndex >= SKILL_MAX_NUM)
return;
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSlotIndex];
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(rkSkillInst.dwIndex, &pSkillData))
return;
if (CPythonSkill::SKILL_TYPE_GUILD == pSkillData->byType)
{
UseGuildSkill(dwSlotIndex);
return;
}
if (!pSkillData->IsCanUseSkill())
return;
if (pSkillData->IsStandingSkill())
{
if (pSkillData->IsToggleSkill())
{
if (IsSkillActive(dwSlotIndex))
{
CInstanceBase * pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain)
return;
if (pkInstMain->IsUsingSkill())
return;
CPythonNetworkStream::Instance().SendUseSkillPacket(rkSkillInst.dwIndex);
return;
}
}
__UseSkill(dwSlotIndex);
return;
}
if (m_dwcurSkillSlotIndex == dwSlotIndex)
{
__UseSkill(m_dwcurSkillSlotIndex);
return;
}
if (!__IsRightButtonSkillMode())
{
__UseSkill(dwSlotIndex);
}
else
{
m_dwcurSkillSlotIndex = dwSlotIndex;
PyCallClassMemberFunc(m_ppyGameWindow, "ChangeCurrentSkill", Py_BuildValue("(i)", dwSlotIndex));
}
}
bool CPythonPlayer::__CheckSkillUsable(DWORD dwSlotIndex)
{
CInstanceBase * pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain)
return false;
if (dwSlotIndex >= SKILL_MAX_NUM)
return false;
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSlotIndex];
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(rkSkillInst.dwIndex, &pSkillData))
return false;
if (pkInstMain->IsMountingHorse())
{
if (!pSkillData->IsHorseSkill())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NOT_HORSE_SKILL"));
return false;
}
}
if (pSkillData->IsHorseSkill())
{
if (!pkInstMain->IsMountingHorse())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "HAVE_TO_RIDE"));
return false;
}
}
// 2004.07.26.levites - 안전지대에서 공격 못하도록 수정
// NOTE : 공격 스킬은 안전지대에서 사용하지 못합니다 - [levites]
if (pSkillData->IsAttackSkill())
{
if (pkInstMain->IsInSafe())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "IN_SAFE"));
return false;
}
}
// NOTE : 패시브 스킬은 사용하지 못합니다 - [levites]
if (!pSkillData->IsCanUseSkill())
return false;
// if (CPythonSkill::SKILL_TYPE_PASSIVE == pSkillData->byType)
// return false;
// NOTE : [Only Assassin] 빈병이 있는지 체크 합니다.
if (pSkillData->IsNeedEmptyBottle())
{
if (!__HasItem(27995))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NEED_EMPTY_BOTTLE"));
return false;
}
}
// NOTE : [Only Assassin] 독병이 있는지 체크 합니다.
if (pSkillData->IsNeedPoisonBottle())
{
if (!__HasItem(27996))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NEED_POISON_BOTTLE"));
return false;
}
}
// NOTE : 낚시 중일때는 스킬을 사용하지 못합니다.
if (pkInstMain->IsFishingMode())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "REMOVE_FISHING_ROD"));
return false;
}
// NOTE : 레벨 체크
if (m_sysIsLevelLimit)
{
if (rkSkillInst.iLevel <= 0)
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NOT_YET_LEARN"));
return false;
}
}
// NOTE : 들고 있는 무기 체크
if (!pSkillData->CanUseWeaponType(pkInstMain->GetWeaponType()))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NOT_MATCHABLE_WEAPON"));
return false;
}
if (!pSkillData->IsHorseSkill()) // HORSE 스킬 중에 화살을 쓰지 않는 스킬이 있기 때문에
{
if (__CheckShortArrow(rkSkillInst, *pSkillData))
return false;
// NOTE : 활이 필요할 경우 화살 개수 체크
if (pSkillData->IsNeedBow())
{
if (!__HasEnoughArrow())
return false;
}
}
if (__CheckDashAffect(*pkInstMain))
{
if (!pSkillData->IsChargeSkill())
{
if (__CheckRestSkillCoolTime(dwSlotIndex))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "WAIT_COOLTIME"));
return false;
}
}
}
else
{
if (__CheckRestSkillCoolTime(dwSlotIndex))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "WAIT_COOLTIME"));
return false;
}
if (__CheckShortLife(rkSkillInst, *pSkillData))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NOT_ENOUGH_HP"));
return false;
}
if (__CheckShortMana(rkSkillInst, *pSkillData))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NOT_ENOUGH_SP"));
return false;
}
}
return true;
}
bool CPythonPlayer::__CheckShortArrow(TSkillInstance & rkSkillInst, CPythonSkill::TSkillData& rkSkillData)
{
if (!rkSkillData.IsNeedBow())
return false;
if (__HasEnoughArrow())
return false;
return true;
}
bool CPythonPlayer::__CheckShortMana(TSkillInstance& rkSkillInst, CPythonSkill::TSkillData& rkSkillData)
{
extern const DWORD c_iSkillIndex_Summon;
if (c_iSkillIndex_Summon == rkSkillInst.dwIndex)
return false;
int iNeedSP = rkSkillData.GetNeedSP(rkSkillInst.fcurEfficientPercentage);
int icurSP = GetStatus(POINT_SP);
// NOTE : ToggleSkill 이 아닌데 소모 SP 가 0 이다.
if (!rkSkillData.IsToggleSkill())
{
if (iNeedSP == 0)
{
CPythonChat::Instance().AppendChat(CHAT_TYPE_INFO, "!!! Find strange game data. Please reinstall metin2.");
return true;
}
}
if (rkSkillData.CanUseIfNotEnough())
{
if (icurSP <= 0)
return true;
}
else
{
if (-1 != iNeedSP)
if (iNeedSP > icurSP)
return true;
}
return false;
}
bool CPythonPlayer::__CheckShortLife(TSkillInstance& rkSkillInst, CPythonSkill::TSkillData& rkSkillData)
{
if (!rkSkillData.IsUseHPSkill())
return false;
DWORD dwNeedHP = rkSkillData.GetNeedSP(rkSkillInst.fcurEfficientPercentage);
if (dwNeedHP <= GetStatus(POINT_HP))
return false;
return true;
}
bool CPythonPlayer::__CheckRestSkillCoolTime(DWORD dwSlotIndex)
{
if (!m_sysIsCoolTime)
return false;
if (dwSlotIndex >= SKILL_MAX_NUM)
return false;
float fElapsedTime = CTimer::Instance().GetCurrentSecond() - m_playerStatus.aSkill[dwSlotIndex].fLastUsedTime;
if (fElapsedTime >= m_playerStatus.aSkill[dwSlotIndex].fCoolTime)
return false;
return true;
}
bool CPythonPlayer::__CheckDashAffect(CInstanceBase& rkInstMain)
{
return rkInstMain.IsAffect(CInstanceBase::AFFECT_DASH);
}
void CPythonPlayer::__UseCurrentSkill()
{
__UseSkill(m_dwcurSkillSlotIndex);
}
DWORD CPythonPlayer::__GetSkillTargetRange(CPythonSkill::TSkillData& rkSkillData)
{
return rkSkillData.GetTargetRange() + GetStatus(POINT_BOW_DISTANCE)*100;
}
bool CPythonPlayer::__ProcessEnemySkillTargetRange(CInstanceBase& rkInstMain, CInstanceBase& rkInstTarget, CPythonSkill::TSkillData& rkSkillData, DWORD dwSkillSlotIndex)
{
DWORD dwSkillTargetRange=__GetSkillTargetRange(rkSkillData);
float fSkillTargetRange = float(dwSkillTargetRange);
if (fSkillTargetRange <= 0.0f)
return true;
// #0000806: [M2EU] 수룡에게 무사(나한군) 탄환격 스킬 사용 안됨
float fTargetDistance = rkInstMain.GetDistance(&rkInstTarget);
extern bool IS_HUGE_RACE(unsigned int vnum);
if (IS_HUGE_RACE(rkInstTarget.GetRace()))
{
fTargetDistance -= 200.0f; // TEMP: 일단 하드 코딩 처리. 정석적으로는 바운드 스피어를 고려해야함
}
if (fTargetDistance >= fSkillTargetRange)
{
if (rkSkillData.IsChargeSkill())
{
if (!__IsReservedUseSkill(dwSkillSlotIndex))
__SendUseSkill(dwSkillSlotIndex, 0);
}
__ReserveUseSkill(rkInstTarget.GetVirtualID(), dwSkillSlotIndex, dwSkillTargetRange);
return false;
}
// 2004.07.05.myevan. 궁신탄영 사용시 맵에 끼임. 사용하기전 갈수 있는곳 체크
TPixelPosition kPPosTarget;
rkInstTarget.NEW_GetPixelPosition(&kPPosTarget);
IBackground& rkBG=IBackground::Instance();
if (rkBG.IsBlock(kPPosTarget.x, kPPosTarget.y))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "CANNOT_APPROACH"));
return false;
}
return true;
}
bool CPythonPlayer::__CanUseSkill()
{
CInstanceBase* pkInstMain=NEW_GetMainActorPtr();
if (!pkInstMain)
return false;
if (IsObserverMode())
return false;
// Fix me
// 뉴마운트. 승마스킬레벨 20 미만인 경우, 고급 마운트를 타고 승마 관련 스킬 못 쓰도록 못하도록 하드 코딩...
// 나중에 시간 나면 can use skill 체크를 서버에서 해주자...
if (pkInstMain->IsMountingHorse() && (GetSkillGrade(109) < 1 && GetSkillLevel(109) < 20))
{
return false;
}
return pkInstMain->CanUseSkill();
}
bool CPythonPlayer::__UseSkill(DWORD dwSlotIndex)
{
// PrivateShop
if (IsOpenPrivateShop())
{
return true;
}
if (!__CanUseSkill())
{
return false;
}
if (dwSlotIndex >= SKILL_MAX_NUM)
{
Tracenf("CPythonPlayer::__UseSkill(dwSlotIndex=%d) It's not available skill slot number", dwSlotIndex);
return false;
}
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSlotIndex];
if (__CheckSpecialSkill(rkSkillInst.dwIndex))
{
return true;
}
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(rkSkillInst.dwIndex, &pSkillData))
{
Tracenf("CPythonPlayer::__UseSkill(dwSlotIndex=%d) There is no skill data", dwSlotIndex);
return false;
}
if (pSkillData->IsToggleSkill())
{
if (IsSkillActive(dwSlotIndex))
{
CPythonNetworkStream::Instance().SendUseSkillPacket(rkSkillInst.dwIndex, dwSlotIndex);
return false;
}
}
if (!__CheckSkillUsable(dwSlotIndex))
{
return false;
}
CInstanceBase * pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain)
{
Tracenf("CPythonPlayer::__UseSkill(dwSlotIndex=%d) There is no main player", dwSlotIndex);
return false;
}
if (pkInstMain->IsUsingSkill())
{
return false;
}
CInstanceBase * pkInstTarget = NULL;
// NOTE : 타겟이 필요한 경우
if (pSkillData->IsNeedTarget() ||
pSkillData->CanChangeDirection() ||
pSkillData->IsAutoSearchTarget())
{
if (pSkillData->IsNeedCorpse())
pkInstTarget=__GetDeadTargetInstancePtr();
else
pkInstTarget=__GetAliveTargetInstancePtr();
// 현재 타겟이 없으면..
if (!pkInstTarget)
{
// 업데이트하고..
__ChangeTargetToPickedInstance();
// 다시 얻어낸다.
if (pSkillData->IsNeedCorpse())
pkInstTarget=__GetDeadTargetInstancePtr();
else
pkInstTarget=__GetAliveTargetInstancePtr();
}
if (pkInstTarget)
{
if (pSkillData->IsOnlyForAlliance())
{
if (pkInstMain == pkInstTarget)
{
if (!pSkillData->CanUseForMe())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "CANNOT_USE_SELF"));
return false;
}
}
else if (!pkInstMain->IsAttackableInstance(*pkInstTarget) && pkInstTarget->IsPC())
{
DWORD dwSkillRange = __GetSkillTargetRange(*pSkillData);
if (dwSkillRange > 0)
{
float fDistance=pkInstMain->GetDistance(pkInstTarget);
if (fDistance>=float(dwSkillRange))
{
__ReserveUseSkill(pkInstTarget->GetVirtualID(), dwSlotIndex, dwSkillRange);
return false;
}
}
}
else
{
if (pSkillData->CanUseForMe())
{
pkInstTarget = pkInstMain;
Tracef(" [ALERT] \265\277\267\341\277\241\260\324 \273\347\277\353\307\317\264\302 \261\342\274\372\300\323\277\241\265\265 \300\373\277\241\260\324 \305\270\260\331\306\303 \265\307\276\356\300\326\276\356\274\255 \300\332\275\305\277\241\260\324\267\316 \300\347\274\263\301\244\n");
}
else
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "ONLY_FOR_ALLIANCE"));
return false;
}
}
}
else
{
if (pkInstTarget->IsInSafe())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "CANNOT_ATTACK_ENEMY_IN_SAFE_AREA"));
return false;
}
if (pkInstMain->IsAttackableInstance(*pkInstTarget))
{
if (!__ProcessEnemySkillTargetRange(*pkInstMain, *pkInstTarget, *pSkillData, dwSlotIndex))
return false;
}
else
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "CANNOT_ATTACK"));
return false;
}
}
pkInstMain->SetFlyTargetInstance(*pkInstTarget);
if (pkInstMain != pkInstTarget)
{
if (pkInstMain->IsFlyTargetObject())
{
pkInstMain->NEW_LookAtFlyTarget();
}
else
{
pkInstMain->NEW_LookAtDestInstance(*pkInstTarget);
}
}
}
else
{
if (pSkillData->IsAutoSearchTarget())
{
if (pkInstMain->NEW_GetFrontInstance(&pkInstTarget, 2000.0f))
{
SetTarget(pkInstTarget->GetVirtualID());
if (!__ProcessEnemySkillTargetRange(*pkInstMain, *pkInstTarget, *pSkillData, dwSlotIndex))
return false;
}
else
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NEED_TARGET"));
return false;
}
}
if (pSkillData->CanUseForMe())
{
pkInstTarget = pkInstMain;
pkInstMain->SetFlyTargetInstance(*pkInstMain);
Tracef(" [ALERT] \305\270\260\331\300\314 \276\370\276\356\274\255 \307\303\267\271\300\314\276\356\277\241\260\324 \273\347\277\353\307\325\264\317\264\331\n");
}
else if (pSkillData->IsNeedCorpse())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "ONLY_FOR_CORPSE"));
return false;
}
else
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NEED_TARGET"));
return false;
}
}
}
if (pSkillData->CanChangeDirection())
{
DWORD dwPickedActorID;
TPixelPosition kPPosPickedGround;
if (pkInstTarget && pkInstTarget!=pkInstMain)
{
pkInstMain->NEW_LookAtDestInstance(*pkInstTarget);
}
else if (__GetPickedActorID(&dwPickedActorID))
{
CInstanceBase* pkInstVictim=NEW_FindActorPtr(dwPickedActorID);
if (pkInstVictim)
pkInstMain->NEW_LookAtDestInstance(*pkInstVictim);
}
else if (__GetPickedGroundPos(&kPPosPickedGround))
{
pkInstMain->NEW_LookAtDestPixelPosition(kPPosPickedGround);
}
else
{
Tracenf("CPythonPlayer::__UseSkill(%d) - \310\255\270\351 \261\342\301\330 \271\346\307\342 \274\263\301\244\300\273 \307\330\276\337\307\324", dwSlotIndex);
}
}
// 관격술 처리
DWORD dwTargetMaxCount = pSkillData->GetTargetCount(rkSkillInst.fcurEfficientPercentage);
DWORD dwRange = __GetSkillTargetRange(*pSkillData);
if (dwTargetMaxCount>0 && pkInstTarget)
{
DWORD dwTargetCount=1;
std::vector<CInstanceBase*> kVct_pkInstTarget;
if (pSkillData->IsFanRange())
{
if (pkInstMain->NEW_GetInstanceVectorInFanRange(float(dwRange), *pkInstTarget, &kVct_pkInstTarget))
{
std::vector<CInstanceBase*>::iterator i;
for (i=kVct_pkInstTarget.begin(); i!=kVct_pkInstTarget.end(); ++i)
{
if (dwTargetCount>=dwTargetMaxCount)
break;
CInstanceBase* pkInstEach=*i;
if (pkInstTarget!=pkInstEach && !pkInstEach->IsDead())
{
pkInstMain->AddFlyTargetInstance(*pkInstEach);
CPythonNetworkStream::Instance().SendAddFlyTargetingPacket(pkInstEach->GetVirtualID(), pkInstEach->GetGraphicThingInstanceRef().OnGetFlyTargetPosition());
dwTargetCount++;
}
}
}
}
else if (pSkillData->IsCircleRange())
{
if (pkInstMain->NEW_GetInstanceVectorInCircleRange(float(dwRange), &kVct_pkInstTarget))
{
std::vector<CInstanceBase*>::iterator i;
for (i=kVct_pkInstTarget.begin(); i!=kVct_pkInstTarget.end(); ++i)
{
if (dwTargetCount>=dwTargetMaxCount)
break;
CInstanceBase* pkInstEach=*i;
if (pkInstTarget!=pkInstEach && !pkInstEach->IsDead())
{
pkInstMain->AddFlyTargetInstance(*pkInstEach);
CPythonNetworkStream::Instance().SendAddFlyTargetingPacket(pkInstEach->GetVirtualID(), pkInstEach->GetGraphicThingInstanceRef().OnGetFlyTargetPosition());
dwTargetCount++;
}
}
}
}
if (dwTargetCount<dwTargetMaxCount)
{
while (dwTargetCount<dwTargetMaxCount)
{
TPixelPosition kPPosDst;
pkInstMain->NEW_GetRandomPositionInFanRange(*pkInstTarget, &kPPosDst);
kPPosDst.x=kPPosDst.x;
kPPosDst.y=-kPPosDst.y;
pkInstMain->AddFlyTargetPosition(kPPosDst);
CPythonNetworkStream::Instance().SendAddFlyTargetingPacket(0, kPPosDst);
dwTargetCount++;
}
}
}
/////
// NOTE : 멀리서 적을 클릭해놓고 스킬을 쓰면 스킬을 쓴뒤 바로 적을 공격하는 문제를 수정하기 위한 코드 - [levites]
__ClearReservedAction();
/////
if (!pSkillData->IsNoMotion())
{
DWORD dwMotionIndex = pSkillData->GetSkillMotionIndex(rkSkillInst.iGrade);
DWORD dwLoopCount = pSkillData->GetMotionLoopCount(rkSkillInst.fcurEfficientPercentage);
if (!pkInstMain->NEW_UseSkill(rkSkillInst.dwIndex, dwMotionIndex, dwLoopCount, pSkillData->IsMovingSkill() ? true : false))
{
Tracenf("CPythonPlayer::__UseSkill(%d) - pkInstMain->NEW_UseSkill - ERROR", dwSlotIndex);
return false;
}
}
DWORD dwTargetVID=pkInstTarget ? pkInstTarget->GetVirtualID() : 0;
__SendUseSkill(dwSlotIndex, dwTargetVID);
return true;
}
void CPythonPlayer::__SendUseSkill(DWORD dwSkillSlotIndex, DWORD dwTargetVID)
{
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSkillSlotIndex];
CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
rkNetStream.SendUseSkillPacket(rkSkillInst.dwIndex, dwTargetVID);
__RunCoolTime(dwSkillSlotIndex);
}
BYTE CPythonPlayer::__GetSkillType(DWORD dwSkillSlotIndex)
{
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSkillSlotIndex];
CPythonSkill::TSkillData * pkSkillData;
CPythonSkill& rkPythonSkill = CPythonSkill::Instance();
if (!rkPythonSkill.GetSkillData(rkSkillInst.dwIndex, &pkSkillData))
{
//TraceError("CPythonPlayer::__GetSkillType(dwSkillSlotIndex=%d) - NOT CHECK", dwSkillSlotIndex);
return 0;
}
return pkSkillData->GetType();
}
void CPythonPlayer::__RunCoolTime(DWORD dwSkillSlotIndex)
{
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSkillSlotIndex];
CPythonSkill::TSkillData * pkSkillData;
if (!CPythonSkill::Instance().GetSkillData(rkSkillInst.dwIndex, &pkSkillData))
{
TraceError("CPythonPlayer::__SendUseSkill(dwSkillSlotIndex=%d) - NOT CHECK", dwSkillSlotIndex);
return;
}
CPythonSkill::TSkillData& rkSkillData=*pkSkillData;
rkSkillInst.fCoolTime = rkSkillData.GetSkillCoolTime(rkSkillInst.fcurEfficientPercentage);
rkSkillInst.fLastUsedTime = CTimer::Instance().GetCurrentSecond();
int iSpd = 100 - GetStatus(POINT_CASTING_SPEED);
if (iSpd > 0)
iSpd = 100 + iSpd;
else if (iSpd < 0)
iSpd = 10000 / (100 - iSpd);
else
iSpd = 100;
rkSkillInst.fCoolTime = rkSkillInst.fCoolTime * iSpd / 100;
PyCallClassMemberFunc(m_ppyGameWindow, "RunUseSkillEvent", Py_BuildValue("(if)", dwSkillSlotIndex, rkSkillInst.fCoolTime));
}
bool CPythonPlayer::__HasEnoughArrow()
{
CItemData * pItemData;
if (CItemManager::Instance().GetItemDataPointer(GetItemIndex(TItemPos(INVENTORY, c_Equipment_Arrow)), &pItemData))
if (CItemData::ITEM_TYPE_WEAPON == pItemData->GetType())
if (CItemData::WEAPON_ARROW == pItemData->GetSubType())
{
return true;
}
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotShotError", Py_BuildValue("(is)", GetMainCharacterIndex(), "EMPTY_ARROW"));
return false;
}
bool CPythonPlayer::__HasItem(DWORD dwItemID)
{
for (int i = 0; i < c_Inventory_Count; ++i)
{
if (dwItemID == GetItemIndex(TItemPos (INVENTORY, i)))
return true;
}
for (int i = 0; i < c_DragonSoul_Inventory_Count; ++i)
{
if (dwItemID == GetItemIndex(TItemPos (DRAGON_SOUL_INVENTORY, i)))
return true;
}
return false;
}
extern const DWORD c_iSkillIndex_Tongsol;
extern const DWORD c_iSkillIndex_Fishing;
extern const DWORD c_iSkillIndex_Mining;
extern const DWORD c_iSkillIndex_Making;
extern const DWORD c_iSkillIndex_Combo;
extern const DWORD c_iSkillIndex_Language1;
extern const DWORD c_iSkillIndex_Language2;
extern const DWORD c_iSkillIndex_Language3;
extern const DWORD c_iSkillIndex_Polymorph;
void CPythonPlayer::UseGuildSkill(DWORD dwSkillSlotIndex)
{
CInstanceBase* pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain)
return;
if (!pkInstMain->CanUseSkill())
return;
///////////////////////////////////////////////////////////////////////////////////////////////
if (dwSkillSlotIndex >= SKILL_MAX_NUM)
{
Tracenf("CPythonPlayer::UseGuildSkill(dwSkillSlotIndex=%d) It's not available skill slot number", dwSkillSlotIndex);
return;
}
TSkillInstance & rkSkillInst = m_playerStatus.aSkill[dwSkillSlotIndex];
DWORD dwSkillIndex = rkSkillInst.dwIndex;
CPythonSkill::TSkillData * pSkillData;
if (!CPythonSkill::Instance().GetSkillData(dwSkillIndex, &pSkillData))
return;
if (__CheckRestSkillCoolTime(dwSkillSlotIndex))
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "WAIT_COOLTIME"));
return;
}
if (pSkillData->IsOnlyForGuildWar())
{
if (!CPythonGuild::Instance().IsDoingGuildWar())
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "ONLY_FOR_GUILD_WAR"));
return;
}
}
DWORD dwMotionIndex = pSkillData->GetSkillMotionIndex();
if (!pkInstMain->NEW_UseSkill(dwSkillIndex, dwMotionIndex, 1, false))
{
Tracenf("CPythonPlayer::UseGuildSkill(%d) - pkInstMain->NEW_UseSkill - ERROR", dwSkillIndex);
}
///////////////////////////////////////////////////////////////////////////////////////////////
CPythonNetworkStream::Instance().SendGuildUseSkillPacket(dwSkillIndex, 0);
__RunCoolTime(dwSkillSlotIndex);
}
void CPythonPlayer::SetComboSkillFlag(BOOL bFlag)
{
DWORD dwSlotIndex;
if (!GetSkillSlotIndex(c_iSkillIndex_Combo, &dwSlotIndex))
{
Tracef("CPythonPlayer::SetComboSkillFlag(killIndex=%d) - Can't Find Slot Index\n", c_iSkillIndex_Combo);
return;
}
int iLevel = GetSkillLevel(dwSlotIndex);
if (iLevel <= 0)
{
Tracef("CPythonPlayer::SetComboSkillFlag(skillIndex=%d, skillLevel=%d) - Invalid Combo Skill Level\n", c_iSkillIndex_Combo, iLevel);
return;
}
iLevel = MIN(iLevel, 2);
CInstanceBase* pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain)
return;
if (bFlag)
{
pkInstMain->SetComboType(iLevel);
__ActivateSkillSlot(dwSlotIndex);
}
else
{
pkInstMain->SetComboType(0);
__DeactivateSkillSlot(dwSlotIndex);
}
}
bool CPythonPlayer::__CheckSpecialSkill(DWORD dwSkillIndex)
{
CInstanceBase* pkInstMain = NEW_GetMainActorPtr();
if (!pkInstMain)
return false;
// Fishing
if (c_iSkillIndex_Fishing == dwSkillIndex)
{
if (pkInstMain->IsFishingMode())
{
NEW_Fishing();
}
else
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "EQUIP_FISHING_ROD"));
}
return true;
}
// Combo
else if (c_iSkillIndex_Combo == dwSkillIndex)
{
DWORD dwSlotIndex;
if (!GetSkillSlotIndex(dwSkillIndex, &dwSlotIndex))
return false;
int iLevel = GetSkillLevel(dwSlotIndex);
if (iLevel > 0)
{
CPythonNetworkStream::Instance().SendUseSkillPacket(dwSkillIndex);
}
else
{
PyCallClassMemberFunc(m_ppyGameWindow, "OnCannotUseSkill", Py_BuildValue("(is)", GetMainCharacterIndex(), "NOT_YET_LEARN"));
}
return true;
}
return false;
}
@@ -46,5 +46,6 @@ void initpack();
void initapp();
void initchr();
void initchrmgr();
void initPlayer();
void initChat();
void initnet();