Files
mtgodot-poc/extension/src/platform/UserInterface/PythonApplication.cpp
T
shenleiandClaude Opus 5.5 3e3708ef06 android-native: auto hunt, in-client account registration, mobile UI polish
- Auto hunt (client-only): PythonPlayerAutoHunt.cpp + AutoHuntScript.inc as the
  embedded mt_autohunt module, AUTO touch button, F9/F8.
- Account registration: tools/40250/register_server.py (rc.d mt_register, :11080)
  inserts into account.account; net.RegisterAccount/GetRegisterAccountResult
  (PythonAccountRegister.cpp) and the mt_register login-window dialog
  (RegisterScript.inc).
- --py-exec also runs in --login-screen mode for scripted login-screen tests.
- Touch controller, window manager and render command updates for the
  Android native client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:15:04 +09:00

738 lines
24 KiB
C++

#include "UserInterface/StdAfx.h"
#include "UserInterface/PythonApplication.h"
#include "UserInterface/MarkManager.h"
#include "GameLib/GameUtil.h"
#include "EterLib/Camera.h"
#include "EterBase/Timer.h"
#include "EterLib/ResourceManager.h"
#include "UserInterface/PythonNetworkStream.h"
#include "UserInterface/AccountConnector.h"
#include "UserInterface/PythonItem.h"
#include "UserInterface/PythonTextTail.h"
#include "UserInterface/PythonSkill.h"
#include "UserInterface/PythonNonPlayer.h"
#include "GameLib/ItemManager.h"
#include "EterLib/NetDevice.h"
#include "../PlatformStub.h"
#include "../EterLib/UIRenderCommands.h"
#include "../EterLib/RenderCommands3D.h"
#include "EterLib/GrpDevice.h"
#include "EterGrnLib/LODController.h"
#include "UserInterface/PythonBackground.h"
#include "UserInterface/PythonSystem.h"
#include "../ScriptLib/PythonBoot.h"
#include "ServerClock.h"
// 2V0-d link surface; 2V0-f adds the application lifecycle (Create/Loop/Process/Exit) that
// prototype.RunApp() drives. The rest of the members still wait for the game slices (2V1-2V3).
//
// PORT: there is no CPythonApplication object yet (its members are the whole game: background,
// network stream, player, ...), so these functions only use the singletons PythonBoot owns
alignas(alignof(CPythonApplication)) static char s_app_storage[sizeof(CPythonApplication)] = {};
CPythonApplication* CPythonApplication::ms_pInstance = reinterpret_cast<CPythonApplication*>(s_app_storage);
extern double g_specularSpd;
void CPythonApplication::ShowWebPage(const char*, const RECT&) { MT_PLATFORM_STUB(); }
void CPythonApplication::MoveWebPage(const RECT&) { MT_PLATFORM_STUB(); }
void CPythonApplication::HideWebPage() { MT_PLATFORM_STUB(); }
bool CPythonApplication::IsWebPageMode() { MT_PLATFORM_STUB(); return false; }
void CPythonApplication::GetInfo(UINT, std::string* out) { MT_PLATFORM_STUB(); if (out) out->clear(); }
// In 40250, Loop() is: while (1) { if (IsMessage()) { if (!MessageProcess()) break; } else { if (!Process()) break; } }
// PORT: Godot owns the message pump and forwards input to the window manager between frames
// (PythonBoot::UIMouseMove & co.), so the loop here only waits for the host's next frame. It runs
// on PythonBoot's script thread; WM_QUIT is PythonBoot::AppRequestQuit.
void CPythonApplication::Loop()
{
while (PythonBoot::AppLoopWait())
{
if (!Process())
break;
}
}
// In 40250 this is PostQuitMessage(0).
void CPythonApplication::Exit() { PythonBoot::AppRequestQuit(); }
// 40250 also writes a TraceError banner first; the caller (system.py) has already logged the reason.
void CPythonApplication::Abort()
{
TraceError("============================================================================================================");
TraceError("Abort!!!!\n\n");
PythonBoot::AppRequestQuit();
}
static float MIN_FOG = 2400.0f;
void CPythonApplication::SetMinFog(float fMinFog) { MIN_FOG = fMinFog; }
// In 40250 Process() copies CTimer::GetCurrentSecond() into m_fGlobalTime right after Advance() and
// this returns it. PORT: no member to hold it; the timer only advances in Process(), so reading it
// here gives the same value.
float CPythonApplication::GetGlobalTime() { return CTimer::Instance().GetCurrentSecond(); }
// In 40250 Process() also copies CTimer::GetElapsedSecond() into m_fGlobalElapsedTime (PythonApplication.cpp:448).
// PORT: read straight from the timer, as GetGlobalTime above.
float CPythonApplication::GetGlobalElapsedTime() { return CTimer::Instance().GetElapsedSecond(); }
static bool s_isFrameSkipDisable = false;
// m_iForceSightRange (the 40250 constructor sets -1: automatic sight range).
static int s_iForceSightRange = -1;
static float s_nativeRenderFrameTime = 0.0f;
void PythonBoot::SetNativeRenderFrameTime(float milliseconds) { s_nativeRenderFrameTime = milliseconds; }
void CPythonApplication::SetFrameSkip(bool isEnable) { s_isFrameSkipDisable = !isEnable; }
// PORT: 40250 creates the Win32 window, the D3D8 device, cursors, sound, keyboard, the net device,
// fonts and the IME here. The Godot window and its canvas already exist when the host starts the
// script, so the window size is the canvas size (PythonBoot::SetUISize) rather than the width/height
// passed from metin2.cfg, and the graphic device is the recording device (RecordingDevice.h) at that
// size. Cursors, sound, keyboard and fonts are not created here yet.
bool CPythonApplication::Create(PyObject*, const char*, int, int, int)
{
unsigned width = 0, height = 0;
UIRenderGetSize(&width, &height);
if (!width || !height)
{
// appCreate returns NULL on failure; RunApp turns this into the original LogBox.
TraceError("CreateDevice: GraphicDevice create failed");
PyErr_SetString(PyExc_RuntimeError, "CREATE_DEVICE");
return false;
}
// 40250 PythonApplication.cpp:1196-1199.
extern bool GRAPHICS_CAPS_SOFTWARE_TILING;
if (!CPythonSystem::Instance().IsAutoTiling())
GRAPHICS_CAPS_SOFTWARE_TILING = CPythonSystem::Instance().IsSoftwareTiling();
// In 40250 m_grpDevice is a member created here (PythonApplication.cpp:909).
static CGraphicDevice s_grpDevice;
int iRet = s_grpDevice.Create(NULL, width, height, true, 32, 0);
switch (iRet)
{
case CGraphicDevice::CREATE_OK:
break;
case CGraphicDevice::CREATE_REFRESHRATE:
break;
case CGraphicDevice::CREATE_GET_DEVICE_CAPS:
PyErr_SetString(PyExc_RuntimeError, "GetDevCaps failed");
TraceError("CreateDevice: GetDevCaps failed");
return false;
case CGraphicDevice::CREATE_GET_DEVICE_CAPS2:
PyErr_SetString(PyExc_RuntimeError, "GetDevCaps2 failed");
TraceError("CreateDevice: GetDevCaps2 failed");
return false;
default:
// PORT: the recording device returns only the cases above; the rest is 40250's fallback.
if (iRet & CGraphicDevice::CREATE_OK)
break;
TraceError("CreateDevice: Unknown Error!");
PyErr_SetString(PyExc_RuntimeError, "UNKNOWN_ERROR");
return false;
}
// 40250 PythonApplication.cpp:1206-1220.
if (CPythonSystem::Instance().IsAutoTiling())
{
if (s_grpDevice.IsFastTNL())
{
CPythonBackground::Instance().ReserveSoftwareTilingEnable(false);
}
else
{
CPythonBackground::Instance().ReserveSoftwareTilingEnable(true);
}
}
else
{
CPythonBackground::Instance().ReserveSoftwareTilingEnable(CPythonSystem::Instance().IsSoftwareTiling());
}
// In 40250 m_netDevice is a member created here (PythonApplication.cpp:1242).
static CNetworkDevice s_netDevice;
if (!s_netDevice.Create())
{
TraceError("NetDevice::Create failed");
PyErr_SetString(PyExc_RuntimeError, "CREATE_NETWORK");
return false;
}
// 40250 PythonApplication.cpp:1250-1251.
if (!s_grpDevice.IsFastTNL())
CGrannyLODController::SetMinLODMode(true);
// 40250 m_pyItem.Create() (PythonApplication.cpp:1253): registers dropitem.mse with CEffectManager.
CPythonItem::Instance().Create();
// 40250 PythonApplication.cpp:1258-1259 (after DefaultFont_Startup).
CPythonIME::Instance().Create(GetWindowHandle());
CPythonIME::Instance().SetText("", 0);
// 40250 PythonApplication.cpp:1260.
CPythonTextTail::Instance().Initialize();
// 40250 PythonApplication.cpp:1262-1263.
// Light Manager
m_LightManager.Initialize();
if (IsNativeTerrainRenderEnabled())
{
const bool use_hardware_cursor = PythonBoot::HostHardwareCursorEnabled() || PythonBoot::TouchInput();
if (CPythonSystem::InstancePtr() && CPythonSystem::Instance().GetConfig())
CPythonSystem::Instance().GetConfig()->is_software_cursor = !use_hardware_cursor;
SetCursorMode(use_hardware_cursor ? CURSOR_MODE_HARDWARE : CURSOR_MODE_SOFTWARE);
CGrannyMaterial::CreateSphereMap(0, "d:/ymir work/special/spheremap.jpg");
CGrannyMaterial::CreateSphereMap(1, "d:/ymir work/special/spheremap01.jpg");
}
return true;
}
// 40250 PythonApplication.cpp:105
void CPythonApplication::GetMousePosition(POINT* ppt)
{
CMSApplication::GetMousePosition(ppt);
}
// 40250 PythonApplication.cpp:168, the !PERF_CHECKER_RENDER_GAME branch (the profiling copy below it
// only adds timers). game.GameWindow.OnRender calls it every frame.
// PORT: the members are reached through their singletons (see the note at the top of the file).
void CPythonApplication::RenderGame()
{
float fAspect=UI::CWindowManager::Instance().GetAspect();
float fFarClip=CPythonBackground::Instance().GetFarClip();
CPythonGraphic::Instance().SetPerspective(30.0f, fAspect, 100.0, fFarClip);
CCullingManager::Instance().Process();
CPythonCharacterManager::Instance().Deform();
CEffectManager::Instance().Update();
CPythonBackground::Instance().RenderCharacterShadowToTexture();
CPythonGraphic::Instance().SetGameRenderState();
CPythonGraphic::Instance().PushState();
{
long lx, ly;
UI::CWindowManager::Instance().GetMousePosition(lx, ly);
// PORT: window coordinates -> the screen coordinates of the full-width 3D view.
lx += UI::CWindowManager::Instance().GetScreenOffsetX();
CPythonGraphic::Instance().SetCursorPosition(lx, ly);
}
CPythonBackground::Instance().RenderSky();
CPythonBackground::Instance().RenderBeforeLensFlare();
CPythonBackground::Instance().RenderCloud();
CPythonBackground::Instance().BeginEnvironment();
CPythonBackground::Instance().Render();
CPythonBackground::Instance().SetCharacterDirLight();
CPythonCharacterManager::Instance().Render();
CPythonBackground::Instance().SetBackgroundDirLight();
CPythonBackground::Instance().RenderWater();
CPythonBackground::Instance().RenderSnow();
CPythonBackground::Instance().RenderEffect();
CPythonBackground::Instance().EndEnvironment();
CEffectManager::Instance().Render();
CPythonItem::Instance().Render();
CFlyingManager::Instance().Render();
CPythonBackground::Instance().BeginEnvironment();
CPythonBackground::Instance().RenderPCBlocker();
CPythonBackground::Instance().EndEnvironment();
CPythonBackground::Instance().RenderAfterLensFlare();
}
// 40250 PythonApplication.cpp:321 without the PERF_CHECKER_RENDER_GAME timers. game.GameWindow.OnUpdate
// calls it every frame. PORT: members through their singletons, as in RenderGame.
void CPythonApplication::UpdateGame()
{
POINT ptMouse;
GetMousePosition(&ptMouse);
CGraphicTextInstance::Hyperlink_UpdateMousePos(ptMouse.x, ptMouse.y);
//!@# Alt+Tab 중 SetTransfor 에서 튕김 현상 해결을 위해 - [levites]
//if (m_isActivateWnd)
{
CScreen s;
float fAspect = UI::CWindowManager::Instance().GetAspect();
float fFarClip = CPythonBackground::Instance().GetFarClip();
s.SetPerspective(30.0f,fAspect, 100.0f, fFarClip);
s.BuildViewFrustum();
}
TPixelPosition kPPosMainActor;
CPythonPlayer::Instance().NEW_GetMainActorPosition(&kPPosMainActor);
CPythonBackground::Instance().Update(kPPosMainActor.x, kPPosMainActor.y, kPPosMainActor.z);
CGameEventManager::Instance().SetCenterPosition(kPPosMainActor.x, kPPosMainActor.y, kPPosMainActor.z);
CGameEventManager::Instance().Update();
CPythonCharacterManager::Instance().Update();
CEffectManager::Instance().UpdateSound();
CFlyingManager::Instance().Update();
CPythonItem::Instance().Update(ptMouse);
CPythonPlayer::Instance().Update();
// NOTE : Update 에서 위치 값이 바뀌므로 다시 얻어 옵니다 - [levites]
// 이 부분 때문에 메인 케릭터의 Sound가 이전 위치에서 플레이 되는 현상이 있었음.
CPythonPlayer::Instance().NEW_GetMainActorPosition(&kPPosMainActor);
SetCenterPosition(kPPosMainActor.x, kPPosMainActor.y, kPPosMainActor.z);
}
// 40250 PythonApplication.cpp:399. Kept in order: timer, network (2V1), input polling (Godot pushes
// input instead), camera (2V3), resource GC, mouse/UI update, then the render block's interface pass.
// PORT: the frame-skip bookkeeping and the game render pass are left out; the Godot frame clock
// decides when this runs, and RenderGame belongs to 2V3.
bool CPythonApplication::Process()
{
ELTimer_SetFrameMSec();
CTimer& rkTimer=CTimer::Instance();
rkTimer.Advance();
// Network I/O
CPythonNetworkStream::Instance().Process();
// PORT: m_kGuildMarkUploader/m_kGuildMarkDownloader.Process() follow here in 40250; the guild mark
// transfer is not ported yet (GuildMarkDownloader.cpp/GuildMarkUploader.cpp).
CAccountConnector::Instance().Process();
//!@# Alt+Tab 중 SetTransfor 에서 튕김 현상 해결을 위해 - [levites]
//if (m_isActivateWnd)
__UpdateCamera();
CResourceManager::Instance().Update();
OnCameraUpdate();
if (IsNativeTerrainRenderEnabled())
OnMouseUpdate();
OnUIUpdate();
if (IsNativeTerrainRenderEnabled())
CGrannyMaterial::TranslateSpecularMatrix(g_specularSpd, g_specularSpd, 0.0f);
// PORT: the render block without the lost-device restore, ClearDepthBuffer and Show (Godot clears
// and presents the frame), and without the !IsActive() SkipRenderBuffering(3000): the host has no
// Win32 activation (CMSWindow::IsActive is a stub). The frame's UI and 3D command lists restart here.
// 40250 PythonApplication.cpp:681.
DWORD dwRenderStartTime = ELTimer_GetMSec();
CCullingManager::Instance().Update();
UIRenderBeginFrame();
Render3DBeginFrame();
CPythonGraphic& rkGraphic = CPythonGraphic::Instance();
if (rkGraphic.Begin())
{
rkGraphic.SetInterfaceRenderState();
OnUIRender();
if (IsNativeTerrainRenderEnabled())
{
rkGraphic.SetInterfaceRenderState();
unsigned cw = 0, ch = 0;
UIRenderGetSize(&cw, &ch);
UIRenderSetClip(0.0f, 0.0f, float(cw), float(ch));
OnMouseRender();
}
rkGraphic.End();
// 40250 PythonApplication.cpp:741-842 (render time, face count, automatic sight range), minus the
// per-second FPS/face counters and the windowed dwBufRenderTime rounding, whose result 40250 never
// uses (its Sleep is commented out).
DWORD dwRenderEndTime = ELTimer_GetMSec();
static DWORD s_dwRenderCheckTime = dwRenderEndTime;
static DWORD s_dwRenderRangeTime = 0;
static DWORD s_dwRenderRangeFrame = 0;
// The native host completes Vulkan work after this command-recording pass.
// Use its last completed frame, including presentation, for automatic sight range.
m_dwCurRenderTime = s_nativeRenderFrameTime > 0.0f
? static_cast<DWORD>(s_nativeRenderFrameTime + 0.5f)
: dwRenderEndTime - dwRenderStartTime;
s_dwRenderRangeTime += m_dwCurRenderTime;
++s_dwRenderRangeFrame;
if (dwRenderEndTime-s_dwRenderCheckTime>1000)
{
m_fAveRenderTime=float(double(s_dwRenderRangeTime)/double(s_dwRenderRangeFrame));
s_dwRenderCheckTime=ELTimer_GetMSec();
s_dwRenderRangeTime=0;
s_dwRenderRangeFrame=0;
}
DWORD dwCurFaceCount=rkGraphic.GetFaceCount();
rkGraphic.ResetFaceCount();
if (dwCurFaceCount > 5000)
{
// 프레임 완충 처리
if (dwRenderEndTime > m_dwBufSleepSkipTime)
{
static float s_fBufRenderTime = 0.0f;
float fCurRenderTime = m_dwCurRenderTime;
if (fCurRenderTime > s_fBufRenderTime)
{
float fRatio = fMAX(0.5f, (fCurRenderTime - s_fBufRenderTime) / 30.0f);
s_fBufRenderTime = (s_fBufRenderTime * (100.0f - fRatio) + (fCurRenderTime + 5) * fRatio) / 100.0f;
}
else
{
float fRatio = 0.5f;
s_fBufRenderTime = (s_fBufRenderTime * (100.0f - fRatio) + fCurRenderTime * fRatio) / 100.0f;
}
// 한계치를 정한다
if (s_fBufRenderTime > 100.0f)
s_fBufRenderTime = 100.0f;
m_fAveRenderTime=s_fBufRenderTime;
}
m_dwFaceAccCount += dwCurFaceCount;
m_dwFaceAccTime += m_dwCurRenderTime;
// PORT: recording a frame can take under 1 ms, so the integer division is guarded (40250's
// D3D frame never took 0 ms).
if (m_dwFaceAccTime)
m_fFaceSpd=(m_dwFaceAccCount/m_dwFaceAccTime);
// 거리 자동 조절
if (-1 == s_iForceSightRange)
{
static float s_fAveRenderTime = 16.0f;
float fRatio=0.3f;
s_fAveRenderTime=(s_fAveRenderTime*(100.0f-fRatio)+max(16.0f, m_dwCurRenderTime)*fRatio)/100.0f;
float fFar=25600.0f;
float fNear=MIN_FOG;
double dbAvePow=double(1000.0f/s_fAveRenderTime);
double dbMaxPow=60.0;
float fDistance=max(fNear+(fFar-fNear)*(dbAvePow)/dbMaxPow, fNear);
CPythonBackground::Instance().SetViewDistanceSet(0, fDistance);
}
// 거리 강제 설정시
else
{
CPythonBackground::Instance().SetViewDistanceSet(0, float(s_iForceSightRange));
}
}
else
{
// 10000 폴리곤 보다 적을때는 가장 멀리 보이게 한다
CPythonBackground::Instance().SetViewDistanceSet(0, 25600.0f);
}
}
return true;
}
// 40250 PythonApplicationCursor.cpp. PORT: the members live here until the application object exists
// (initial values from the constructor and CreateCursors); the host draws no hardware cursor, so every
// shape in [0, CURSOR_COUNT) counts as loaded in place of m_CursorHandleMap.
PyObject* s_poMouseHandler = NULL; // m_poMouseHandler
namespace
{
int s_iCursorNum = CPythonApplication::CURSOR_SHAPE_NORMAL; // m_iCursorNum
int s_iContinuousCursorNum = CPythonApplication::CURSOR_SHAPE_NORMAL; // m_iContinuousCursorNum
BOOL s_bCursorVisible = TRUE; // m_bCursorVisible
bool s_bLiarCursorOn = false; // m_bLiarCursorOn
int s_iCursorMode = CPythonApplication::CURSOR_MODE_HARDWARE; // m_iCursorMode
}
void CPythonApplication::SetCursorVisible(BOOL bFlag, bool bLiarCursorOn)
{
s_bCursorVisible = bFlag;
// PORT: a touch screen has no cursor, not even the stand-in 40250 draws while the real one is hidden.
s_bLiarCursorOn = bLiarCursorOn && !PythonBoot::TouchInput();
// PORT: 40250 also counts ShowCursor up or down in hardware mode; the host has no OS cursor to hide.
}
BOOL CPythonApplication::GetCursorVisible()
{
return s_bCursorVisible;
}
bool CPythonApplication::GetLiarCursorOn()
{
return s_bLiarCursorOn;
}
int CPythonApplication::GetCursorMode()
{
return s_iCursorMode;
}
int CPythonApplication::GetCursorNum()
{
return s_iCursorNum;
}
BOOL CPythonApplication::__IsContinuousChangeTypeCursor(int iCursorNum)
{
switch (iCursorNum)
{
case CURSOR_SHAPE_NORMAL:
case CURSOR_SHAPE_ATTACK:
case CURSOR_SHAPE_TARGET:
case CURSOR_SHAPE_MAGIC:
case CURSOR_SHAPE_BUY:
case CURSOR_SHAPE_SELL:
return TRUE;
break;
}
return FALSE;
}
BOOL CPythonApplication::SetCursorNum(int iCursorNum)
{
if (CURSOR_SHAPE_NORMAL == iCursorNum)
{
if (!__IsContinuousChangeTypeCursor(s_iCursorNum))
{
iCursorNum = s_iContinuousCursorNum;
}
}
else
{
if (__IsContinuousChangeTypeCursor(s_iCursorNum))
{
s_iContinuousCursorNum = s_iCursorNum;
}
}
if (CURSOR_MODE_HARDWARE == s_iCursorMode)
{
// PORT: m_CursorHandleMap.find(iCursorNum) and SetCursor(hCursor).
if (iCursorNum < 0 || iCursorNum >= CURSOR_COUNT)
return FALSE;
}
s_iCursorNum = iCursorNum;
PyCallClassMemberFunc(s_poMouseHandler, "ChangeCursor", Py_BuildValue("(i)", s_iCursorNum));
return TRUE;
}
void CPythonApplication::SetCursorMode(int iMode)
{
switch (iMode)
{
case CURSOR_MODE_HARDWARE:
s_iCursorMode = CURSOR_MODE_HARDWARE;
break;
case CURSOR_MODE_SOFTWARE:
s_iCursorMode = CURSOR_MODE_SOFTWARE;
break;
}
}
// 40250 PythonApplication.cpp:871.
void CPythonApplication::SetMouseHandler(PyObject* poMouseHandler)
{
s_poMouseHandler = poMouseHandler;
}
void CPythonApplication::SetGlobalCenterPosition(LONG x, LONG y)
{
// 40250 PythonApplication.cpp:1284: the background owns the global-to-local map offset.
CPythonBackground& background = CPythonBackground::Instance();
background.GlobalPositionToLocalPosition(x, y);
SetCenterPosition(x, y, background.GetHeight(x, y));
}
void CPythonApplication::SetFPS(int) {}
namespace {
DWORD s_server_start_msec = 0;
time_t s_server_time = 0;
time_t s_local_start_time = 0;
}
void SetPlatformServerTime(time_t server_time)
{
s_server_start_msec = ELTimer_GetMSec();
s_server_time = server_time;
s_local_start_time = time(nullptr);
}
time_t GetPlatformServerTime()
{
return static_cast<time_t>(ELTimer_GetMSec() - s_server_start_msec) + s_server_time;
}
time_t GetPlatformServerTimeStamp()
{
return (time(nullptr) - s_local_start_time) + s_server_time;
}
void CPythonApplication::SetServerTime(time_t server_time) { SetPlatformServerTime(server_time); }
time_t CPythonApplication::GetServerTime() { return GetPlatformServerTime(); }
time_t CPythonApplication::GetServerTimeStamp() { return GetPlatformServerTimeStamp(); }
void CPythonApplication::SetConnectData(const char*, int) { MT_PLATFORM_STUB(); }
void CPythonApplication::GetConnectData(std::string& ip, int& port) { MT_PLATFORM_STUB(); ip.clear(); port = 0; }
// 40250 PythonApplication.cpp:1377.
void CPythonApplication::SetForceSightRange(int iRange)
{
s_iForceSightRange = iRange;
}
int CPythonApplication::CheckDeviceState()
{
return DEVICE_STATE_OK;
}
bool CPythonApplication::CreateDevice(int width, int height, int Windowed, int bit, int frequency)
{
static CGraphicDevice s_grpDevice;
return s_grpDevice.Create(NULL, width, height, Windowed ? true : false, bit, frequency) == CGraphicDevice::CREATE_OK;
}
void CPythonApplication::NotifyHack(const char* c_szFormat, ...)
{
char szBuf[1024];
va_list args;
va_start(args, c_szFormat);
vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args);
va_end(args);
CPythonNetworkStream::Instance().NotifyHack(szBuf);
}
// 40250 PythonApplication.cpp:394
void CPythonApplication::SkipRenderBuffering(DWORD dwSleepMSec)
{
m_dwBufSleepSkipTime=ELTimer_GetMSec()+dwSleepMSec;
}
void CPythonApplication::UpdateClientRect()
{
unsigned w = 0, h = 0;
UIRenderGetSize(&w, &h);
UI::CWindowManager::Instance().SetResolution(w, h);
}
int CPythonApplication::GetWidth()
{
unsigned w = 0, h = 0;
UIRenderGetSize(&w, &h);
return (int)w;
}
int CPythonApplication::GetHeight()
{
unsigned w = 0, h = 0;
UIRenderGetSize(&w, &h);
return (int)h;
}
void CPythonApplication::Clear()
{
CPythonSystem::Instance().Clear();
}
void CPythonApplication::Destroy()
{
}
void CMovieMan::PlayLogo(const char*) { MT_PLATFORM_STUB(); }
BOOL PERF_CHECKER_RENDER_GAME = FALSE;
double g_specularSpd = 0;
// 40250 UserInterface/PythonApplication.cpp, verbatim.
// SUPPORT_NEW_KOREA_SERVER
bool LoadLocaleData(const char* localePath)
{
NANOBEGIN
CPythonNonPlayer& rkNPCMgr = CPythonNonPlayer::Instance();
CItemManager& rkItemMgr = CItemManager::Instance();
CPythonSkill& rkSkillMgr = CPythonSkill::Instance();
CPythonNetworkStream& rkNetStream = CPythonNetworkStream::Instance();
char szItemList[256];
char szItemProto[256];
char szItemDesc[256];
char szMobProto[256];
char szSkillDescFileName[256];
char szSkillTableFileName[256];
char szInsultList[256];
snprintf(szItemList, sizeof(szItemList) , "%s/item_list.txt", localePath);
snprintf(szItemProto, sizeof(szItemProto), "%s/item_proto", localePath);
snprintf(szItemDesc, sizeof(szItemDesc), "%s/itemdesc.txt", localePath);
snprintf(szMobProto, sizeof(szMobProto), "%s/mob_proto", localePath);
snprintf(szSkillDescFileName, sizeof(szSkillDescFileName), "%s/SkillDesc.txt", localePath);
snprintf(szSkillTableFileName, sizeof(szSkillTableFileName), "%s/SkillTable.txt", localePath);
snprintf(szInsultList, sizeof(szInsultList), "%s/insult.txt", localePath);
rkNPCMgr.Destroy();
rkItemMgr.Destroy();
rkSkillMgr.Destroy();
if (!rkItemMgr.LoadItemList(szItemList))
{
TraceError("LoadLocaleData - LoadItemList(%s) Error", szItemList);
}
if (!rkItemMgr.LoadItemTable(szItemProto))
{
TraceError("LoadLocaleData - LoadItemProto(%s) Error", szItemProto);
return false;
}
if (!rkItemMgr.LoadItemDesc(szItemDesc))
{
Tracenf("LoadLocaleData - LoadItemDesc(%s) Error", szItemDesc);
}
if (!rkNPCMgr.LoadNonPlayerData(szMobProto))
{
TraceError("LoadLocaleData - LoadMobProto(%s) Error", szMobProto);
return false;
}
if (!rkSkillMgr.RegisterSkillDesc(szSkillDescFileName))
{
TraceError("LoadLocaleData - RegisterSkillDesc(%s) Error", szMobProto);
return false;
}
if (!rkSkillMgr.RegisterSkillTable(szSkillTableFileName))
{
TraceError("LoadLocaleData - RegisterSkillTable(%s) Error", szMobProto);
return false;
}
if (!rkNetStream.LoadInsultList(szInsultList))
{
Tracenf("CPythonApplication - CPythonNetworkStream::LoadInsultList(%s)", szInsultList);
}
if (LocaleService_IsYMIR())
{
char szEmpireTextConvFile[256];
for (DWORD dwEmpireID=1; dwEmpireID<=3; ++dwEmpireID)
{
sprintf(szEmpireTextConvFile, "%s/lang%d.cvt", localePath, dwEmpireID);
if (!rkNetStream.LoadConvertTable(dwEmpireID, szEmpireTextConvFile))
{
TraceError("LoadLocaleData - CPythonNetworkStream::LoadConvertTable(%d, %s) FAILURE", dwEmpireID, szEmpireTextConvFile);
}
}
}
NANOEND
return true;
}