Port 2V0-f window input and UI render command bridge
This commit is contained in:
@@ -504,3 +504,4 @@
|
||||
{"time": "2026-09-23T02:17:52Z", "event": "port_round", "unit": "PORT-PLAN 批次 2V0-b: window tree completion and 64-bit LZO boundary repair", "ported": ["EterPythonLib/PythonWindow.cpp (211 functions)", "EterPythonLib/PythonWindowManager.cpp (76)", "EterPythonLib/PythonSlotWindow.cpp (80)", "EterPythonLib/PythonGridSlotWindow.cpp (14)", "EterLib/ReferenceObject.cpp (9)", "EterLib/Resource.cpp (18)", "EterLib/ResourceManager.cpp (25)"], "adapted": ["EterBase/lzo.cpp: CLZObject::Compress and CLZObject::Decompress use native-width lzo_uint at the liblzo API while retaining 32-bit pack-header lengths"], "divergent": [], "needs_live": [], "status_note": "The seven units in b4c9d6ce are exact mechanical copies of 40250 (0 manual edits); their 433 functions are recorded in port-map. The original port.window_tree test had four expectations contrary to 40250: DeactivateWindow restores the previous active window and SetTop changes update order; corrected the expectations. A fresh 64-bit build exposed stack corruption in the older LZO caller because Win32 UINT (4 bytes) was passed as native lzo_uint* (8 bytes); fixed the boundary. The normal app path still does not load the UI shell; module registration and rendering remain 2V0-c through 2V0-f.", "tests": ["port_copy.py diff on seven units: 0 manual edits", "ctest -R port.window_tree: pass", "ctest -R port.eterpack: pass, including added LZO compress/decompress round trip and full pack inventory", "ctest -R port.python_launcher: pass", "ctest --test-dir build --output-on-failure -j6: 28/28 pass", "port_map.py check: 0 errors"], "not_done": "2V0-c through 2V0-f; normal app startup and visible interactive Logo/Popup are the whole 2V0 acceptance gate, not the 2V0-b substep.", "commit": "2V0-b-completion"}
|
||||
{"time": "2026-09-23T02:57:22Z", "event": "port_round", "unit": "PORT-PLAN 2V0-c/d and beginning of 2V0-e", "ported": ["EterPythonLib/PythonWindowManagerModule.cpp", "EterPythonLib/PythonGraphicModule.cpp", "EterPythonLib/PythonGraphicImageModule.cpp", "EterPythonLib/PythonGraphicTextModule.cpp", "EterPythonLib/PythonGraphicThingModule.cpp", "UserInterface/PythonApplicationModule.cpp", "ScriptLib/PythonDebugModule.cpp", "42 UserInterface dependency headers"], "adapted": ["Python pointer handles via PyLong_FromVoidPtr/PyLong_AsVoidPtr (native pointer width)", "Win32 FindFirstFile/FindNextFile shim for app file enumeration", "Godot PythonBoot registers dbg, grp, grpImage, grpText, wndMgr and app and propagates dbg.LogBox failures"], "divergent": ["CPythonApplication platform lifecycle and graphics methods remain MT_PLATFORM_STUB; no visible Logo/Popup yet", "DevIL image and DirectShow logo backends remain MT_PLATFORM_STUB"], "tests": ["cmake --build build -> pass", "ctest --test-dir build --output-on-failure -j6 -> 29/29 passed, 1 skipped", "godot --headless --path project --script python_host_test.gd -> system.py reaches constInfo.py import net and reports ImportError through RunMainScript", "port_map.py check -> 0 errors"], "not_done": "2V0-e native gameplay module stubs and prototype.RunApp; 2V0-f Godot UI renderer/input/Logo/Popup runtime acceptance", "status_note": "The app binding imports, but its CPythonApplication adapter is only a link surface. The 2V0 acceptance gate is not yet met."}
|
||||
{"time": "2026-09-23T03:04:18Z", "event": "port_round", "unit": "PORT-PLAN 2V0-e: native gameplay import stubs through prototype.RunApp", "ported": [], "adapted": ["27 native module import surfaces generated from 40250 UserInterface export names; functions log module.method and return a placeholder zero", "LocaleService default en/1252 from supplied Client/locale.cfg; ForceSetLocale stores requested path", "CPythonApplication::Create platform stub raises CREATE_DEVICE so prototype.RunApp handles failure as original Python code expects"], "divergent": ["Gameplay module methods and constants are placeholders, not ports; their calls do not implement gameplay", "The app platform adapter does not create a device, so RunApp exits before Logo/Popup"], "tests": ["godot --headless --path project --script python_host_test.gd -> PASS: system.py reaches prototype.RunApp, catches CREATE_DEVICE, and net/player/item are marked __port_stub__", "ctest --test-dir build --output-on-failure -j6 -> 29/29 passed, one skipped", "port_map.py check -> 0 errors"], "not_done": "2V0-f: real Godot UI adapter, visible interactive Logo/Popup, input/focus/clipping/text/image tests"}
|
||||
{"time": "2026-09-23T03:27:45Z", "event": "port_round", "unit": "PORT-PLAN 2V0-f: Godot window input and Canvas UI command adapter (partial)", "ported": [], "adapted": ["40250 CWindowManager mouse/key/focus/update bridge into Godot", "CGraphicScreen Bar/Line viewport clip and CGraphicTextInstance text render commands", "CGraphicImage and CGraphicImageInstance image command generation; CPythonResource registration; TGA/DDS/PNG dimensions", "Godot Canvas Control renders native commands and decodes TGA images from 40250 pack", "CGraphicSubImage .sub registration, referenced image dimensions and crop bounds", "CGraphicExpandedImageInstance allocation, type identity and lifetime for script bindings"], "divergent": ["CPythonApplication::Create and Loop are still platform stubs: real system.py cannot show Logo/Popup", "Text metrics, fonts, vertical clipping and non-TGA packed image variants are not verified at parity", "ExpandedImage Canvas rendering still omits original rotation, scale, rendering rectangle and blend mode", "Exploratory monkeypatch past app.Create reaches logo setup but stopping that second RunApp crashes during Python finalization; lifecycle ownership remains unresolved"], "needs_live": [], "tests": ["cmake --build build --target mtgodot -j6: pass", "godot --headless --path project --script python_host_test.gd: pass input/focus/update, bar, clip, text, packed TGA texture, .sub and ExpandedImage create/delete", "ctest --test-dir build --output-on-failure -j6: 29/29 pass, one skipped", "git diff --check: pass"], "not_done": "2V0-f application lifecycle adapter and real visible interactive Logo/Popup; normal app route remains legacy and 2V0 acceptance gate is open", "status_note": "No audit/port-map entry changed. The skill mentions scripts/port_map.py, but that file is absent from this checkout; no port-map check was run."}
|
||||
|
||||
+1
-1
@@ -400,7 +400,7 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
| 2V0-c | `wndMgr` / `grp` / `grpImage` / `grpText` / `grpThing` 模块 | 绑定已移植并通过导入/窗口句柄测试;图形绘制仍属 2V0-f |
|
||||
| 2V0-d | 42 个 `UserInterface` 头(都在 `PythonApplication.h` 后面)+ `PythonApplicationModule.cpp`(`app`) | 头与绑定已编译、链接并导入;应用生命周期 adapter 仍属 2V0-f |
|
||||
| 2V0-e | `dbg` 等桩模块补齐,`system.py` 跑到 `prototype.RunApp()` | 完成:27 个玩法 native 模块导出名桩记录每次调用;真实 `system.py` 已进入 `RunApp()`,目前由待实现的 `app.Create()` 报 `CREATE_DEVICE` |
|
||||
| 2V0-f | Godot 渲染器上的 platform UI adapter;Logo/Popup 可显示可操作 | 未做 |
|
||||
| 2V0-f | Godot 渲染器上的 platform UI adapter;Logo/Popup 可显示可操作 | 进行中:40250 窗口树输入/焦点/更新和 Bar、Line、裁剪、TextLine、ImageBox 命令已接 Godot Canvas;pack TGA、`.sub` 和 ExpandedImage 创建路径在 app 进程内测试。ExpandedImage 的变换绘制仍待实现;`CPythonApplication::Create`/`Loop` 仍是平台桩,真实 `system.py` 尚未显示 Logo/Popup,验收未通过。 |
|
||||
2. **2V1 — 登录/选角**:加入 `PythonNetworkStream{,PhaseHandShake,PhaseLogin,PhaseSelect,PhaseLoading}.cpp`、
|
||||
`PythonNetworkStreamModule.cpp`、AccountConnector 及登录/选角脚本真实调用到的模块。验收为 FakeServer 离线完成 phase 顺序,
|
||||
再以真服 smoke 证明登录、角色列表和选择进入 Loading;未验证的服务器分支记 `NEEDS_LIVE`。
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "EterLib/GrpBase.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "UIRenderCommands.h"
|
||||
|
||||
auto CGraphicBase::GetAvailableTextureMemory() -> DWORD
|
||||
{
|
||||
@@ -208,9 +209,9 @@ auto CGraphicBase::SetViewport(DWORD, DWORD, DWORD, DWORD, float, float) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicBase::GetBackBufferSize(UINT *, UINT *) -> void
|
||||
auto CGraphicBase::GetBackBufferSize(UINT *width, UINT *height) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
UIRenderGetSize(width, height);
|
||||
}
|
||||
|
||||
auto CGraphicBase::IsTLVertexClipping() -> bool
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
#include "EterLib/GrpExpandedImageInstance.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "EterBase/CRC32.h"
|
||||
|
||||
auto CGraphicExpandedImageInstance::Type() -> DWORD
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<DWORD>();
|
||||
static DWORD type = GetCRC32("CGraphicExpandedImageInstance", sizeof("CGraphicExpandedImageInstance") - 1);
|
||||
return type;
|
||||
}
|
||||
|
||||
CGraphicExpandedImageInstance::CGraphicExpandedImageInstance()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
CGraphicExpandedImageInstance::~CGraphicExpandedImageInstance()
|
||||
@@ -23,63 +24,69 @@ CGraphicExpandedImageInstance::~CGraphicExpandedImageInstance()
|
||||
|
||||
auto CGraphicExpandedImageInstance::Destroy() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
CGraphicImageInstance::Destroy();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetDepth(float) -> void
|
||||
auto CGraphicExpandedImageInstance::SetDepth(float depth) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_fDepth = depth;
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetOrigin() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_v2Origin = D3DXVECTOR2(GetWidth() * 0.5f, GetHeight() * 0.5f);
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetOrigin(float, float) -> void
|
||||
auto CGraphicExpandedImageInstance::SetOrigin(float x, float y) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_v2Origin = D3DXVECTOR2(x, y);
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetRotation(float) -> void
|
||||
auto CGraphicExpandedImageInstance::SetRotation(float rotation) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_fRotation = rotation;
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetScale(float, float) -> void
|
||||
auto CGraphicExpandedImageInstance::SetScale(float x, float y) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_v2Scale = D3DXVECTOR2(x, y);
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetRenderingRect(float, float, float, float) -> void
|
||||
auto CGraphicExpandedImageInstance::SetRenderingRect(float left, float top, float right, float bottom) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_RenderingRect = {static_cast<LONG>(left), static_cast<LONG>(top),
|
||||
static_cast<LONG>(right), static_cast<LONG>(bottom)};
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::SetRenderingMode(int) -> void
|
||||
auto CGraphicExpandedImageInstance::SetRenderingMode(int mode) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_iRenderingMode = mode;
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::Initialize() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_fDepth = 0;
|
||||
m_v2Origin = D3DXVECTOR2(0, 0);
|
||||
m_v2Scale = D3DXVECTOR2(1, 1);
|
||||
m_fRotation = 0;
|
||||
m_RenderingRect = {0, 0, 0, 0};
|
||||
m_iRenderingMode = RENDERING_MODE_NORMAL;
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::OnRender() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
CGraphicImageInstance::OnRender();
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::OnSetImagePointer() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!IsEmpty()) SetOrigin();
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::OnIsType(DWORD) -> BOOL
|
||||
auto CGraphicExpandedImageInstance::OnIsType(DWORD type) -> BOOL
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<BOOL>();
|
||||
return type == Type() || CGraphicImageInstance::OnIsType(type);
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::CreateSystem(UINT) -> void
|
||||
@@ -94,13 +101,14 @@ auto CGraphicExpandedImageInstance::DestroySystem() -> void
|
||||
|
||||
auto CGraphicExpandedImageInstance::New() -> CGraphicExpandedImageInstance *
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<CGraphicExpandedImageInstance *>();
|
||||
return new CGraphicExpandedImageInstance();
|
||||
}
|
||||
|
||||
auto CGraphicExpandedImageInstance::Delete(CGraphicExpandedImageInstance *) -> void
|
||||
auto CGraphicExpandedImageInstance::Delete(CGraphicExpandedImageInstance *instance) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!instance) return;
|
||||
instance->Destroy();
|
||||
delete instance;
|
||||
}
|
||||
|
||||
decltype(CGraphicExpandedImageInstance::ms_kPool) CGraphicExpandedImageInstance::ms_kPool{};
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
auto CGraphicImage::Type() -> TType
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<TType>();
|
||||
static TType type = StringToType("CGraphicImage");
|
||||
return type;
|
||||
}
|
||||
|
||||
CGraphicImage::CGraphicImage(const char * c_szFileName, DWORD dwFilter) : CResource(c_szFileName), m_dwFilter(dwFilter) // initializers as in 40250
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_rect = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
CGraphicImage::~CGraphicImage()
|
||||
@@ -34,53 +34,49 @@ auto CGraphicImage::DestroyDeviceObjects() -> void
|
||||
|
||||
auto CGraphicImage::GetWidth() const -> int
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<int>();
|
||||
return m_rect.right - m_rect.left;
|
||||
}
|
||||
|
||||
auto CGraphicImage::GetHeight() const -> int
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<int>();
|
||||
return m_rect.bottom - m_rect.top;
|
||||
}
|
||||
|
||||
auto CGraphicImage::GetRectReference() const -> const RECT &
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<const RECT &>();
|
||||
return m_rect;
|
||||
}
|
||||
|
||||
auto CGraphicImage::GetTextureReference() const -> const CGraphicTexture &
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<const CGraphicTexture &>();
|
||||
return m_imageTexture;
|
||||
}
|
||||
|
||||
auto CGraphicImage::GetTexturePointer() -> CGraphicTexture *
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<CGraphicTexture *>();
|
||||
return &m_imageTexture;
|
||||
}
|
||||
|
||||
auto CGraphicImage::OnLoad(int, const void *) -> bool
|
||||
auto CGraphicImage::OnLoad(int size, const void *data) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
m_imageTexture.SetFileName(GetFileName());
|
||||
if (!m_imageTexture.CreateFromMemoryFile(size, data, D3DFMT_UNKNOWN, m_dwFilter)) return false;
|
||||
m_rect = {0, 0, m_imageTexture.GetWidth(), m_imageTexture.GetHeight()};
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CGraphicImage::OnClear() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_imageTexture.Destroy();
|
||||
m_rect = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
auto CGraphicImage::OnIsEmpty() const -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
return m_imageTexture.IsEmpty();
|
||||
}
|
||||
|
||||
auto CGraphicImage::OnIsType(TType) -> bool
|
||||
auto CGraphicImage::OnIsType(TType type) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
return type == Type() || CResource::OnIsType(type);
|
||||
}
|
||||
|
||||
@@ -4,22 +4,25 @@
|
||||
#include "EterLib/GrpImageInstance.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "UIRenderCommands.h"
|
||||
#include "EterBase/CRC32.h"
|
||||
#include <algorithm>
|
||||
|
||||
auto CGraphicImageInstance::Type() -> DWORD
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<DWORD>();
|
||||
static DWORD type = GetCRC32("CGraphicImageInstance", sizeof("CGraphicImageInstance") - 1);
|
||||
return type;
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::IsType(DWORD) -> BOOL
|
||||
auto CGraphicImageInstance::IsType(DWORD type) -> BOOL
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<BOOL>();
|
||||
return OnIsType(type);
|
||||
}
|
||||
|
||||
CGraphicImageInstance::CGraphicImageInstance()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_DiffuseColor = D3DXCOLOR(1, 1, 1, 1);
|
||||
m_v2Position = D3DXVECTOR2(0, 0);
|
||||
}
|
||||
|
||||
CGraphicImageInstance::~CGraphicImageInstance()
|
||||
@@ -29,27 +32,28 @@ CGraphicImageInstance::~CGraphicImageInstance()
|
||||
|
||||
auto CGraphicImageInstance::Destroy() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_roImage.Clear();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::Render() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!IsEmpty()) OnRender();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::SetDiffuseColor(float, float, float, float) -> void
|
||||
auto CGraphicImageInstance::SetDiffuseColor(float r, float g, float b, float a) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_DiffuseColor = D3DXCOLOR(r, g, b, a);
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::SetPosition(float, float) -> void
|
||||
auto CGraphicImageInstance::SetPosition(float x, float y) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_v2Position = D3DXVECTOR2(x, y);
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::SetImagePointer(CGraphicImage *) -> void
|
||||
auto CGraphicImageInstance::SetImagePointer(CGraphicImage *image) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_roImage.SetPointer(image);
|
||||
OnSetImagePointer();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::ReloadImagePointer(CGraphicImage *) -> void
|
||||
@@ -59,20 +63,17 @@ auto CGraphicImageInstance::ReloadImagePointer(CGraphicImage *) -> void
|
||||
|
||||
auto CGraphicImageInstance::IsEmpty() const -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
return m_roImage.IsNull() || m_roImage->IsEmpty();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::GetWidth() -> int
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<int>();
|
||||
return IsEmpty() ? 0 : m_roImage->GetWidth();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::GetHeight() -> int
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<int>();
|
||||
return IsEmpty() ? 0 : m_roImage->GetHeight();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::GetTexturePointer() -> CGraphicTexture *
|
||||
@@ -89,8 +90,7 @@ auto CGraphicImageInstance::GetTextureReference() const -> const CGraphicTexture
|
||||
|
||||
auto CGraphicImageInstance::GetGraphicImagePointer() -> CGraphicImage *
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<CGraphicImage *>();
|
||||
return m_roImage.GetPointer();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::operator==(const CGraphicImageInstance &) const -> bool
|
||||
@@ -106,7 +106,18 @@ auto CGraphicImageInstance::Initialize() -> void
|
||||
|
||||
auto CGraphicImageInstance::OnRender() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
CGraphicImage* image = m_roImage.GetPointer();
|
||||
if (!image) return;
|
||||
auto channel = [](float value) -> std::uint32_t {
|
||||
return static_cast<std::uint32_t>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
|
||||
};
|
||||
const std::uint32_t color = (channel(m_DiffuseColor.a) << 24) |
|
||||
(channel(m_DiffuseColor.r) << 16) | (channel(m_DiffuseColor.g) << 8) |
|
||||
channel(m_DiffuseColor.b);
|
||||
UIRenderCommand command{UIRenderCommand::Image, m_v2Position.x, m_v2Position.y,
|
||||
m_v2Position.x + image->GetWidth(), m_v2Position.y + image->GetHeight(), color};
|
||||
command.text = image->GetFileName();
|
||||
UIRenderAdd(std::move(command));
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::OnSetImagePointer() -> void
|
||||
@@ -114,10 +125,9 @@ auto CGraphicImageInstance::OnSetImagePointer() -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::OnIsType(DWORD) -> BOOL
|
||||
auto CGraphicImageInstance::OnIsType(DWORD type) -> BOOL
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<BOOL>();
|
||||
return type == Type();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::CreateSystem(UINT) -> void
|
||||
@@ -132,13 +142,14 @@ auto CGraphicImageInstance::DestroySystem() -> void
|
||||
|
||||
auto CGraphicImageInstance::New() -> CGraphicImageInstance *
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<CGraphicImageInstance *>();
|
||||
return new CGraphicImageInstance();
|
||||
}
|
||||
|
||||
auto CGraphicImageInstance::Delete(CGraphicImageInstance *) -> void
|
||||
auto CGraphicImageInstance::Delete(CGraphicImageInstance *instance) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!instance) return;
|
||||
instance->Destroy();
|
||||
delete instance;
|
||||
}
|
||||
|
||||
decltype(CGraphicImageInstance::ms_kPool) CGraphicImageInstance::ms_kPool{};
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
#include "EterLib/GrpImageTexture.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
unsigned little16(const unsigned char* data) { return data[0] | (unsigned(data[1]) << 8); }
|
||||
unsigned little32(const unsigned char* data) { return little16(data) | (little16(data + 2) << 16); }
|
||||
unsigned big32(const unsigned char* data) { return (unsigned(data[0]) << 24) | (unsigned(data[1]) << 16) | (unsigned(data[2]) << 8) | data[3]; }
|
||||
}
|
||||
|
||||
CGraphicImageTexture::CGraphicImageTexture()
|
||||
{
|
||||
@@ -17,7 +24,7 @@ CGraphicImageTexture::~CGraphicImageTexture()
|
||||
|
||||
auto CGraphicImageTexture::Destroy() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
CGraphicTexture::Destroy();
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::Create(UINT, UINT, D3DFORMAT, DWORD) -> bool
|
||||
@@ -32,9 +39,12 @@ auto CGraphicImageTexture::CreateDeviceObjects() -> bool
|
||||
return mt_platform_stub_return<bool>();
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::CreateFromTexturePointer(const CGraphicTexture *) -> void
|
||||
auto CGraphicImageTexture::CreateFromTexturePointer(const CGraphicTexture *source) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!source) { Destroy(); return; }
|
||||
m_width = source->GetWidth();
|
||||
m_height = source->GetHeight();
|
||||
m_bEmpty = source->IsEmpty();
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::CreateFromDiskFile(const char *, D3DFORMAT, DWORD) -> bool
|
||||
@@ -43,10 +53,22 @@ auto CGraphicImageTexture::CreateFromDiskFile(const char *, D3DFORMAT, DWORD) ->
|
||||
return mt_platform_stub_return<bool>();
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::CreateFromMemoryFile(UINT, const void *, D3DFORMAT, DWORD) -> bool
|
||||
auto CGraphicImageTexture::CreateFromMemoryFile(UINT size, const void *bytes, D3DFORMAT, DWORD) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
if (!bytes) return false;
|
||||
const auto* data = static_cast<const unsigned char*>(bytes);
|
||||
unsigned width = 0, height = 0;
|
||||
if (size >= 20 && std::memcmp(data, "DDS ", 4) == 0) {
|
||||
height = little32(data + 12); width = little32(data + 16);
|
||||
} else if (size >= 24 && std::memcmp(data, "\x89PNG\r\n\x1a\n", 8) == 0) {
|
||||
width = big32(data + 16); height = big32(data + 20);
|
||||
} else if (size >= 18 && (data[2] == 2 || data[2] == 10 || data[2] == 3)) {
|
||||
width = little16(data + 12); height = little16(data + 14);
|
||||
}
|
||||
if (!width || !height || width > 16384 || height > 16384) return false;
|
||||
m_width = static_cast<int>(width); m_height = static_cast<int>(height);
|
||||
m_bEmpty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::CreateDDSTexture(CDXTCImage &, const BYTE *) -> bool
|
||||
@@ -55,9 +77,9 @@ auto CGraphicImageTexture::CreateDDSTexture(CDXTCImage &, const BYTE *) -> bool
|
||||
return mt_platform_stub_return<bool>();
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::SetFileName(const char *) -> void
|
||||
auto CGraphicImageTexture::SetFileName(const char *name) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_stFileName = name ? name : "";
|
||||
}
|
||||
|
||||
auto CGraphicImageTexture::Lock(int *, void **, int) -> bool
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "EterLib/GrpScreen.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "UIRenderCommands.h"
|
||||
#include <algorithm>
|
||||
|
||||
CScreen::CScreen()
|
||||
{
|
||||
@@ -51,9 +53,9 @@ auto CScreen::Show(RECT *, HWND) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CScreen::RenderLine2d(float, float, float, float, float) -> void
|
||||
auto CScreen::RenderLine2d(float sx, float sy, float ex, float ey, float) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
UIRenderAdd({UIRenderCommand::Line, sx, sy, ex, ey, ms_diffuseColor});
|
||||
}
|
||||
|
||||
auto CScreen::RenderBox2d(float, float, float, float, float) -> void
|
||||
@@ -61,9 +63,9 @@ auto CScreen::RenderBox2d(float, float, float, float, float) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CScreen::RenderBar2d(float, float, float, float, float) -> void
|
||||
auto CScreen::RenderBar2d(float sx, float sy, float ex, float ey, float) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
UIRenderAdd({UIRenderCommand::Bar, sx, sy, ex, ey, ms_diffuseColor});
|
||||
}
|
||||
|
||||
auto CScreen::RenderGradationBar2d(float, float, float, float, DWORD, DWORD, float) -> void
|
||||
@@ -181,14 +183,17 @@ auto CScreen::SetAddColorOperation(D3DXCOLOR &) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CScreen::SetDiffuseColor(DWORD) -> void
|
||||
auto CScreen::SetDiffuseColor(DWORD color) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
ms_diffuseColor = color;
|
||||
}
|
||||
|
||||
auto CScreen::SetDiffuseColor(float, float, float, float) -> void
|
||||
auto CScreen::SetDiffuseColor(float r, float g, float b, float a) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
auto channel = [](float value) -> DWORD {
|
||||
return static_cast<DWORD>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
|
||||
};
|
||||
ms_diffuseColor = (channel(a) << 24) | (channel(r) << 16) | (channel(g) << 8) | channel(b);
|
||||
}
|
||||
|
||||
auto CScreen::SetClearColor(float, float, float, float) -> void
|
||||
|
||||
@@ -1,79 +1,100 @@
|
||||
// Platform skeleton for EterLib/GrpSubImage.h (40250 EterLib/GrpSubImage.cpp), generated by platform_stub.py.
|
||||
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
|
||||
#include "EterLib/StdAfx.h"
|
||||
#include "EterLib/GrpSubImage.h"
|
||||
#include "EterLib/ResourceManager.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
char CGraphicSubImage::m_SearchPath[256] = "D:/Ymir Work/UI/";
|
||||
|
||||
auto CGraphicSubImage::Type() -> TType
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<TType>();
|
||||
static TType type = StringToType("CGraphicSubImage");
|
||||
return type;
|
||||
}
|
||||
|
||||
decltype(CGraphicSubImage::m_SearchPath) CGraphicSubImage::m_SearchPath{};
|
||||
CGraphicSubImage::CGraphicSubImage(const char* name) : CGraphicImage(name) {}
|
||||
CGraphicSubImage::~CGraphicSubImage() { m_roImage.Clear(); }
|
||||
|
||||
CGraphicSubImage::CGraphicSubImage(const char * c_szFileName) : CGraphicImage(c_szFileName) // initializers as in 40250
|
||||
bool CGraphicSubImage::CreateDeviceObjects()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (m_roImage.IsNull()) return false;
|
||||
m_imageTexture.CreateFromTexturePointer(m_roImage->GetTexturePointer());
|
||||
return true;
|
||||
}
|
||||
|
||||
CGraphicSubImage::~CGraphicSubImage()
|
||||
void CGraphicSubImage::SetImagePointer(CGraphicImage* image)
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_roImage.SetPointer(image);
|
||||
CreateDeviceObjects();
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::CreateDeviceObjects() -> bool
|
||||
bool CGraphicSubImage::SetImageFileName(const char* name)
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
CResource* resource = CResourceManager::Instance().GetResourcePointer(name);
|
||||
if (!resource || !resource->IsType(CGraphicImage::Type())) return false;
|
||||
SetImagePointer(static_cast<CGraphicImage*>(resource));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::SetImageFileName(const char *) -> bool
|
||||
void CGraphicSubImage::SetRectPosition(int left, int top, int right, int bottom)
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
m_rect = {left, top, right, bottom};
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::SetRectPosition(int, int, int, int) -> void
|
||||
void CGraphicSubImage::SetRectReference(const RECT& rect) { m_rect = rect; }
|
||||
|
||||
void CGraphicSubImage::SetSearchPath(const char* path)
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!path) return;
|
||||
std::strncpy(m_SearchPath, path, sizeof(m_SearchPath) - 1);
|
||||
m_SearchPath[sizeof(m_SearchPath) - 1] = 0;
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::SetRectReference(const RECT &) -> void
|
||||
bool CGraphicSubImage::OnLoad(int size, const void* bytes)
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!bytes || size <= 0) return false;
|
||||
std::istringstream input(std::string(static_cast<const char*>(bytes), static_cast<size_t>(size)));
|
||||
std::map<std::string, std::string> tokens;
|
||||
std::string line;
|
||||
while (std::getline(input, line)) {
|
||||
std::istringstream fields(line);
|
||||
std::string key, value;
|
||||
if (!(fields >> key >> value)) continue;
|
||||
if (!value.empty() && value.front() == '"') {
|
||||
value.erase(0, 1);
|
||||
if (!value.empty() && value.back() == '"') value.pop_back();
|
||||
}
|
||||
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
tokens[key] = value;
|
||||
}
|
||||
if (tokens["title"] != "subimage" || tokens["image"].empty()) return false;
|
||||
std::string image_path;
|
||||
if (tokens["version"] == "2.0") {
|
||||
const std::string parent = GetFileNameString();
|
||||
const auto slash = parent.find_last_of("\\/");
|
||||
image_path = slash == std::string::npos ? tokens["image"] : parent.substr(0, slash + 1) + tokens["image"];
|
||||
} else {
|
||||
image_path = std::string(m_SearchPath) + tokens["image"];
|
||||
}
|
||||
if (!SetImageFileName(image_path.c_str())) return false;
|
||||
SetRectPosition(std::atoi(tokens["left"].c_str()), std::atoi(tokens["top"].c_str()),
|
||||
std::atoi(tokens["right"].c_str()), std::atoi(tokens["bottom"].c_str()));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::SetSearchPath(const char *) -> void
|
||||
void CGraphicSubImage::OnClear()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_roImage.Clear();
|
||||
m_imageTexture.Destroy();
|
||||
m_rect = {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::SetImagePointer(CGraphicImage *) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::OnLoad(int, const void *) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::OnClear() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::OnIsEmpty() const -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
}
|
||||
|
||||
auto CGraphicSubImage::OnIsType(TType) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
}
|
||||
bool CGraphicSubImage::OnIsEmpty() const { return m_roImage.IsNull() || m_roImage->IsEmpty(); }
|
||||
bool CGraphicSubImage::OnIsType(TType type) { return type == Type() || CGraphicImage::OnIsType(type); }
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include "EterLib/GrpTextInstance.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "UIRenderCommands.h"
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
auto CGraphicTextInstance::Hyperlink_UpdateMousePos(int, int) -> void
|
||||
{
|
||||
@@ -18,7 +21,14 @@ auto CGraphicTextInstance::Hyperlink_GetText(char *, int) -> int
|
||||
|
||||
CGraphicTextInstance::CGraphicTextInstance()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_dwTextColor = 0xffc7c7c7;
|
||||
m_hAlign = HORIZONTAL_ALIGN_LEFT;
|
||||
m_vAlign = VERTICAL_ALIGN_TOP;
|
||||
m_v3Position = D3DXVECTOR3(0, 0, 0);
|
||||
m_isSecret = false;
|
||||
m_isMultiLine = false;
|
||||
m_isOutline = false;
|
||||
m_isCursor = false;
|
||||
}
|
||||
|
||||
CGraphicTextInstance::~CGraphicTextInstance()
|
||||
@@ -28,17 +38,19 @@ CGraphicTextInstance::~CGraphicTextInstance()
|
||||
|
||||
auto CGraphicTextInstance::Destroy() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_stText.clear();
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::Update() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
// The Canvas adapter measures and rasterizes text during its draw pass.
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::Render(RECT *) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
UIRenderCommand command{UIRenderCommand::Text, m_v3Position.x, m_v3Position.y, 0, 0, m_dwTextColor};
|
||||
command.text = m_isSecret ? std::string(m_stText.size(), '*') : m_stText;
|
||||
UIRenderAdd(std::move(command));
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::ShowCursor() -> void
|
||||
@@ -61,14 +73,17 @@ auto CGraphicTextInstance::HideOutLine() -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetColor(DWORD) -> void
|
||||
auto CGraphicTextInstance::SetColor(DWORD color) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_dwTextColor = color;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetColor(float, float, float, float) -> void
|
||||
auto CGraphicTextInstance::SetColor(float r, float g, float b, float a) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
auto channel = [](float value) -> DWORD {
|
||||
return static_cast<DWORD>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
|
||||
};
|
||||
m_dwTextColor = (channel(a) << 24) | (channel(r) << 16) | (channel(g) << 8) | channel(b);
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetOutLineColor(DWORD) -> void
|
||||
@@ -81,14 +96,14 @@ auto CGraphicTextInstance::SetOutLineColor(float, float, float, float) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetHorizonalAlign(int) -> void
|
||||
auto CGraphicTextInstance::SetHorizonalAlign(int align) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_hAlign = align;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetVerticalAlign(int) -> void
|
||||
auto CGraphicTextInstance::SetVerticalAlign(int align) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_vAlign = align;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetMax(int) -> void
|
||||
@@ -101,24 +116,26 @@ auto CGraphicTextInstance::SetTextPointer(CGraphicText *) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetValueString(const std::string &) -> void
|
||||
auto CGraphicTextInstance::SetValueString(const std::string &value) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_stText = value;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetValue(const char *, size_t) -> void
|
||||
auto CGraphicTextInstance::SetValue(const char *value, size_t len) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!value) { m_stText.clear(); return; }
|
||||
if (len == static_cast<size_t>(-1)) m_stText = value;
|
||||
else m_stText.assign(value, len);
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetPosition(float, float, float) -> void
|
||||
auto CGraphicTextInstance::SetPosition(float x, float y, float z) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_v3Position = D3DXVECTOR3(x, y, z);
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetSecret(bool) -> void
|
||||
auto CGraphicTextInstance::SetSecret(bool secret) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_isSecret = secret;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::SetOutline(bool) -> void
|
||||
@@ -141,15 +158,15 @@ auto CGraphicTextInstance::SetLimitWidth(float) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::GetTextSize(int *, int *) -> void
|
||||
auto CGraphicTextInstance::GetTextSize(int *width, int *height) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (width) *width = static_cast<int>(m_stText.size()) * 8;
|
||||
if (height) *height = 16;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::GetValueStringReference() -> const std::string &
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<const std::string &>();
|
||||
return m_stText;
|
||||
}
|
||||
|
||||
auto CGraphicTextInstance::GetTextLineCount() -> WORD
|
||||
|
||||
@@ -7,20 +7,17 @@
|
||||
|
||||
auto CGraphicTexture::IsEmpty() const -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
return m_bEmpty;
|
||||
}
|
||||
|
||||
auto CGraphicTexture::GetWidth() const -> int
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<int>();
|
||||
return m_width;
|
||||
}
|
||||
|
||||
auto CGraphicTexture::GetHeight() const -> int
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<int>();
|
||||
return m_height;
|
||||
}
|
||||
|
||||
auto CGraphicTexture::SetTextureStage(int) const -> void
|
||||
@@ -41,7 +38,9 @@ auto CGraphicTexture::DestroyDeviceObjects() -> void
|
||||
|
||||
CGraphicTexture::CGraphicTexture()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_bEmpty = true;
|
||||
m_width = m_height = 0;
|
||||
m_lpd3dTexture = nullptr;
|
||||
}
|
||||
|
||||
CGraphicTexture::~CGraphicTexture()
|
||||
@@ -51,7 +50,8 @@ CGraphicTexture::~CGraphicTexture()
|
||||
|
||||
auto CGraphicTexture::Destroy() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
m_bEmpty = true;
|
||||
m_width = m_height = 0;
|
||||
}
|
||||
|
||||
auto CGraphicTexture::Initialize() -> void
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "UIRenderCommands.h"
|
||||
|
||||
namespace {
|
||||
std::vector<UIRenderCommand> commands;
|
||||
int canvas_width = 0;
|
||||
int canvas_height = 0;
|
||||
float clip_x1 = 0, clip_y1 = 0, clip_x2 = 0, clip_y2 = 0;
|
||||
float saved_x1 = 0, saved_y1 = 0, saved_x2 = 0, saved_y2 = 0;
|
||||
}
|
||||
|
||||
void UIRenderSetSize(int width, int height) { canvas_width = width; canvas_height = height; }
|
||||
void UIRenderGetSize(unsigned* width, unsigned* height) {
|
||||
if (width) *width = canvas_width;
|
||||
if (height) *height = canvas_height;
|
||||
}
|
||||
void UIRenderBeginFrame() {
|
||||
commands.clear();
|
||||
clip_x1 = clip_y1 = 0;
|
||||
clip_x2 = canvas_width;
|
||||
clip_y2 = canvas_height;
|
||||
}
|
||||
void UIRenderAdd(UIRenderCommand command) {
|
||||
command.clip_x1 = clip_x1; command.clip_y1 = clip_y1;
|
||||
command.clip_x2 = clip_x2; command.clip_y2 = clip_y2;
|
||||
commands.push_back(command);
|
||||
}
|
||||
const std::vector<UIRenderCommand>& UIRenderCommands() { return commands; }
|
||||
void UIRenderSetClip(float x, float y, float width, float height) {
|
||||
saved_x1 = clip_x1; saved_y1 = clip_y1; saved_x2 = clip_x2; saved_y2 = clip_y2;
|
||||
clip_x1 = x; clip_y1 = y;
|
||||
clip_x2 = x + width; clip_y2 = y + height;
|
||||
}
|
||||
void UIRenderRestoreClip() {
|
||||
clip_x1 = saved_x1; clip_y1 = saved_y1;
|
||||
clip_x2 = saved_x2; clip_y2 = saved_y2;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Commands emitted by the 40250 UI drawing calls, consumed by Godot's CanvasItem.
|
||||
// The port layer stays independent of godot-cpp.
|
||||
struct UIRenderCommand {
|
||||
enum Kind { Bar, Line, Text, Image } kind;
|
||||
float x1, y1, x2, y2;
|
||||
std::uint32_t argb;
|
||||
float clip_x1 = 0, clip_y1 = 0, clip_x2 = 0, clip_y2 = 0;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
void UIRenderSetSize(int width, int height);
|
||||
void UIRenderGetSize(unsigned* width, unsigned* height);
|
||||
void UIRenderBeginFrame();
|
||||
void UIRenderAdd(UIRenderCommand command);
|
||||
const std::vector<UIRenderCommand>& UIRenderCommands();
|
||||
void UIRenderSetClip(float x, float y, float width, float height);
|
||||
void UIRenderRestoreClip();
|
||||
@@ -10,9 +10,10 @@
|
||||
#include "EterPythonLib/PythonGraphic.h"
|
||||
|
||||
#include "../PlatformStub.h"
|
||||
#include "../EterLib/UIRenderCommands.h"
|
||||
|
||||
void CPythonGraphic::SetViewport(float, float, float, float) { MT_PLATFORM_STUB(); }
|
||||
void CPythonGraphic::RestoreViewport() { MT_PLATFORM_STUB(); }
|
||||
void CPythonGraphic::SetViewport(float x, float y, float width, float height) { UIRenderSetClip(x, y, width, height); }
|
||||
void CPythonGraphic::RestoreViewport() { UIRenderRestoreClip(); }
|
||||
void CPythonGraphic::SetOmniLight() { MT_PLATFORM_STUB(); }
|
||||
long CPythonGraphic::GenerateColor(float, float, float, float) { MT_PLATFORM_STUB(); return 0; }
|
||||
void CPythonGraphic::RenderUpButton(float, float, float, float) { MT_PLATFORM_STUB(); }
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// split so the launcher can outlive the call that creates it.
|
||||
#include "ScriptLib/StdAfx.h"
|
||||
#include "ScriptLib/PythonLauncher.h"
|
||||
#include "ScriptLib/Resource.h"
|
||||
#include "UserInterface/StdAfx.h" // initpack and the rest of the module initializer list
|
||||
#include "EterPythonLib/StdAfx.h"
|
||||
#include "PythonBoot.h"
|
||||
#include "PythonHost.h"
|
||||
#include "../EterLib/UIRenderCommands.h"
|
||||
#include "../EterBase/LogBox.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -15,6 +17,7 @@ namespace
|
||||
// 40250 has this on Main()'s stack. CPythonLauncher is a CSingleton, so there is still only ever one.
|
||||
std::unique_ptr<CPythonLauncher> g_launcher;
|
||||
std::unique_ptr<UI::CWindowManager> g_window_manager;
|
||||
std::unique_ptr<CPythonResource> g_resource;
|
||||
|
||||
bool fail(std::string* error, const std::string& text)
|
||||
{
|
||||
@@ -34,6 +37,50 @@ bool IsRunning()
|
||||
return g_launcher != nullptr;
|
||||
}
|
||||
|
||||
void SetUISize(int width, int height)
|
||||
{
|
||||
if (!g_window_manager || width <= 0 || height <= 0) return;
|
||||
g_window_manager->SetScreenSize(width, height);
|
||||
g_window_manager->SetResolution(width, height);
|
||||
UIRenderSetSize(width, height);
|
||||
}
|
||||
|
||||
void UIMouseMove(int x, int y)
|
||||
{
|
||||
if (g_window_manager) g_window_manager->RunMouseMove(x, y);
|
||||
}
|
||||
|
||||
void UIMouseButton(int button, bool pressed, int x, int y)
|
||||
{
|
||||
if (!g_window_manager) return;
|
||||
g_window_manager->RunMouseMove(x, y);
|
||||
switch (button)
|
||||
{
|
||||
case 1: if (pressed) g_window_manager->RunMouseLeftButtonDown(x, y); else g_window_manager->RunMouseLeftButtonUp(x, y); break;
|
||||
case 2: if (pressed) g_window_manager->RunMouseRightButtonDown(x, y); else g_window_manager->RunMouseRightButtonUp(x, y); break;
|
||||
case 3: if (pressed) g_window_manager->RunMouseMiddleButtonDown(x, y); else g_window_manager->RunMouseMiddleButtonUp(x, y); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void UIKey(int key, bool pressed)
|
||||
{
|
||||
if (!g_window_manager) return;
|
||||
if (pressed) g_window_manager->RunKeyDown(key);
|
||||
else g_window_manager->RunKeyUp(key);
|
||||
}
|
||||
|
||||
void UIUpdate()
|
||||
{
|
||||
if (g_window_manager) g_window_manager->Update();
|
||||
}
|
||||
|
||||
void UIRender()
|
||||
{
|
||||
UIRenderBeginFrame();
|
||||
if (g_window_manager) g_window_manager->Render();
|
||||
}
|
||||
|
||||
bool Start(const char* stdlib_path, std::string* error)
|
||||
{
|
||||
if (g_launcher)
|
||||
@@ -59,6 +106,7 @@ bool Start(const char* stdlib_path, std::string* error)
|
||||
Stop();
|
||||
return fail(error, "InstallStdLib(" + stdlib + "): " + stdlib_error);
|
||||
}
|
||||
g_resource = std::make_unique<CPythonResource>();
|
||||
g_window_manager = std::make_unique<UI::CWindowManager>();
|
||||
return true;
|
||||
}
|
||||
@@ -158,6 +206,7 @@ void Stop()
|
||||
if (!g_launcher)
|
||||
return;
|
||||
g_window_manager.reset();
|
||||
g_resource.reset();
|
||||
g_launcher->Clear(); // Main() calls Clear() explicitly before the launcher leaves scope
|
||||
g_launcher.reset();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,15 @@ bool RunMainScript(const char* lpCmdLine, std::string* error);
|
||||
// no 40250 packs, which is where RunMainScript cannot run at all.
|
||||
bool RunLine(const char* source, std::string* error);
|
||||
|
||||
// Forward physical Godot input to the original 40250 window manager. Coordinates
|
||||
// are viewport pixels; SetUISize also establishes its logical resolution.
|
||||
void SetUISize(int width, int height);
|
||||
void UIMouseMove(int x, int y);
|
||||
void UIMouseButton(int button, bool pressed, int x, int y);
|
||||
void UIKey(int key, bool pressed);
|
||||
void UIUpdate();
|
||||
void UIRender();
|
||||
|
||||
// 40250 Main() calls Clear() and lets the launcher leave scope.
|
||||
void Stop();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "ScriptLib/StdAfx.h"
|
||||
#include "ScriptLib/Resource.h"
|
||||
#include "EterLib/GrpImage.h"
|
||||
#include "EterLib/GrpSubImage.h"
|
||||
|
||||
namespace {
|
||||
CResource* NewUIImage(const char* name) { return new CGraphicImage(name); }
|
||||
CResource* NewUISubImage(const char* name) { return new CGraphicSubImage(name); }
|
||||
}
|
||||
|
||||
CPythonResource::CPythonResource() {
|
||||
for (const char* ext : {"tga", "dds", "png", "jpg", "bmp"})
|
||||
m_resManager.RegisterResourceNewFunctionPointer(ext, NewUIImage);
|
||||
m_resManager.RegisterResourceNewFunctionPointer("sub", NewUISubImage);
|
||||
}
|
||||
CPythonResource::~CPythonResource() { m_resManager.DestroyDeletingList(); }
|
||||
void CPythonResource::Destroy() { m_resManager.DestroyDeletingList(); m_resManager.Destroy(); }
|
||||
void CPythonResource::DumpFileList(const char* name) { m_resManager.DumpFileListToTextFile(name); }
|
||||
@@ -8,9 +8,12 @@
|
||||
// Standard types only — PythonBoot.h keeps <Python-2.7/*> and the Win32 shims out of this translation
|
||||
// unit, which has to stay compilable next to godot-cpp.
|
||||
#include "platform/ScriptLib/PythonBoot.h"
|
||||
#include "platform/EterLib/UIRenderCommands.h"
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include <godot_cpp/variant/dictionary.hpp>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
namespace mtgodot {
|
||||
@@ -52,6 +55,37 @@ String Metin2PythonHost::run_line(const String &source) {
|
||||
return String();
|
||||
}
|
||||
|
||||
void Metin2PythonHost::set_ui_size(int width, int height) { PythonBoot::SetUISize(width, height); }
|
||||
void Metin2PythonHost::ui_mouse_move(int x, int y) { PythonBoot::UIMouseMove(x, y); }
|
||||
void Metin2PythonHost::ui_mouse_button(int button, bool pressed, int x, int y) {
|
||||
PythonBoot::UIMouseButton(button, pressed, x, y);
|
||||
}
|
||||
void Metin2PythonHost::ui_key(int key, bool pressed) { PythonBoot::UIKey(key, pressed); }
|
||||
void Metin2PythonHost::ui_update() { PythonBoot::UIUpdate(); }
|
||||
Array Metin2PythonHost::ui_render_commands() {
|
||||
PythonBoot::UIRender();
|
||||
Array out;
|
||||
for (const auto &command : UIRenderCommands()) {
|
||||
Dictionary item;
|
||||
item["kind"] = command.kind == UIRenderCommand::Bar ? "bar" :
|
||||
(command.kind == UIRenderCommand::Line ? "line" :
|
||||
(command.kind == UIRenderCommand::Image ? "image" : "text"));
|
||||
item["x1"] = command.x1;
|
||||
item["y1"] = command.y1;
|
||||
item["x2"] = command.x2;
|
||||
item["y2"] = command.y2;
|
||||
item["argb"] = static_cast<int64_t>(command.argb);
|
||||
item["clip_x1"] = command.clip_x1;
|
||||
item["clip_y1"] = command.clip_y1;
|
||||
item["clip_x2"] = command.clip_x2;
|
||||
item["clip_y2"] = command.clip_y2;
|
||||
if (command.kind == UIRenderCommand::Text || command.kind == UIRenderCommand::Image)
|
||||
item["text"] = String::utf8(command.text.c_str());
|
||||
out.push_back(item);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void Metin2PythonHost::stop() {
|
||||
PythonBoot::Stop();
|
||||
}
|
||||
@@ -76,6 +110,13 @@ String Metin2PythonHost::run_line(const String &) {
|
||||
return String("this build has no embedded Python (-DMTGODOT_EMBED_PYTHON=ON)");
|
||||
}
|
||||
|
||||
void Metin2PythonHost::set_ui_size(int, int) {}
|
||||
void Metin2PythonHost::ui_mouse_move(int, int) {}
|
||||
void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {}
|
||||
void Metin2PythonHost::ui_key(int, bool) {}
|
||||
void Metin2PythonHost::ui_update() {}
|
||||
Array Metin2PythonHost::ui_render_commands() { return Array(); }
|
||||
|
||||
void Metin2PythonHost::stop() {
|
||||
}
|
||||
|
||||
@@ -88,6 +129,12 @@ void Metin2PythonHost::_bind_methods() {
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("run_main_script", "command_line"),
|
||||
&Metin2PythonHost::run_main_script, DEFVAL(String()));
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("run_line", "source"), &Metin2PythonHost::run_line);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("set_ui_size", "width", "height"), &Metin2PythonHost::set_ui_size);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_move", "x", "y"), &Metin2PythonHost::ui_mouse_move);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_button", "button", "pressed", "x", "y"), &Metin2PythonHost::ui_mouse_button);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("stop"), &Metin2PythonHost::stop);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
#include <godot_cpp/variant/string.hpp>
|
||||
#include <godot_cpp/variant/array.hpp>
|
||||
|
||||
namespace mtgodot {
|
||||
|
||||
@@ -29,6 +30,12 @@ public:
|
||||
static godot::String run_main_script(const godot::String &command_line);
|
||||
// One line of Python in the same __main__ dictionary. "" when it ran.
|
||||
static godot::String run_line(const godot::String &source);
|
||||
static void set_ui_size(int width, int height);
|
||||
static void ui_mouse_move(int x, int y);
|
||||
static void ui_mouse_button(int button, bool pressed, int x, int y);
|
||||
static void ui_key(int key, bool pressed);
|
||||
static void ui_update();
|
||||
static godot::Array ui_render_commands();
|
||||
static void stop();
|
||||
|
||||
protected:
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
# 桌面:python_host_test.gd(--script);真机:MT_TEST_MODE=python 从 client_main.gd 进来。
|
||||
extends RefCounted
|
||||
|
||||
const PythonUISurface = preload("res://python_ui_surface.gd")
|
||||
|
||||
const EXPECTED_STOP := "Sorry, Your system does not support 3D graphics"
|
||||
|
||||
var fail := 0
|
||||
@@ -54,6 +56,104 @@ func run() -> int:
|
||||
"system.py 进入 prototype.RunApp(),Create() 报告待实现的图形设备")
|
||||
_ck(Metin2PythonHost.run_line("import net, player, item; assert net.__port_stub__ == 1 and player.__port_stub__ == 1 and item.__port_stub__ == 1") == "",
|
||||
"2V0 玩法模块桩已注册,调用会记录到 stderr")
|
||||
# 真正从 Godot 入口转发事件给 40250 的 CWindowManager,再由窗口树回调 Python。
|
||||
Metin2PythonHost.set_ui_size(800, 600)
|
||||
var probe_source := """
|
||||
import wndMgr
|
||||
_ui_events = []
|
||||
class _UIProbe:
|
||||
def OnMouseOverIn(self): _ui_events.append('in')
|
||||
def OnMouseOverOut(self): _ui_events.append('out')
|
||||
def OnMouseOver(self): pass
|
||||
def OnMouseLeftButtonDown(self): _ui_events.append('down')
|
||||
def OnMouseLeftButtonUp(self): _ui_events.append('up'); return 1
|
||||
def OnSetFocus(self): _ui_events.append('focus')
|
||||
def OnKillFocus(self): _ui_events.append('blur')
|
||||
def OnUpdate(self): _ui_events.append('update')
|
||||
def OnKeyDown(self, key): _ui_events.append('key:%d' % key); return 1
|
||||
def OnKeyUp(self, key): return 1
|
||||
_ui_probe = _UIProbe()
|
||||
_ui_window = wndMgr.Register(_ui_probe, 'UI')
|
||||
wndMgr.SetWindowSize(_ui_window, 100, 50)
|
||||
wndMgr.SetWindowPosition(_ui_window, 10, 20)
|
||||
wndMgr.Show(_ui_window)
|
||||
wndMgr.SetFocus(_ui_window)
|
||||
"""
|
||||
_ck(Metin2PythonHost.run_line(probe_source) == "", "40250 窗口测试对象已在 Godot 进程中创建")
|
||||
Metin2PythonHost.ui_mouse_move(15, 25)
|
||||
Metin2PythonHost.ui_mouse_button(1, true, 15, 25)
|
||||
Metin2PythonHost.ui_mouse_button(1, false, 15, 25)
|
||||
Metin2PythonHost.ui_key(42, true)
|
||||
Metin2PythonHost.ui_update()
|
||||
_ck(Metin2PythonHost.run_line("assert 'in' in _ui_events and 'down' in _ui_events and 'up' in _ui_events and 'focus' in _ui_events and 'key:42' in _ui_events and 'update' in _ui_events") == "",
|
||||
"Godot 输入、焦点和更新事件抵达 40250 Python 窗口")
|
||||
_ck(Metin2PythonHost.run_line("_ui_bar = wndMgr.RegisterBar(_UIProbe(), 'UI'); wndMgr.SetWindowSize(_ui_bar, 20, 10); wndMgr.SetWindowPosition(_ui_bar, 30, 40); wndMgr.SetColor(_ui_bar, 0x7f336699); wndMgr.Show(_ui_bar)") == "",
|
||||
"40250 Bar 窗口已创建")
|
||||
var draws: Array = Metin2PythonHost.ui_render_commands()
|
||||
var has_bar := false
|
||||
for draw in draws:
|
||||
if draw["kind"] == "bar" and draw["x1"] == 30.0 and draw["y1"] == 40.0 and draw["x2"] == 50.0 and draw["y2"] == 50.0 and draw["argb"] == 0x7f336699:
|
||||
has_bar = true
|
||||
_ck(has_bar, "40250 Bar 绘制调用进入 Godot Canvas 命令队列")
|
||||
var clip_source := """
|
||||
import grp
|
||||
class _ClipProbe(_UIProbe):
|
||||
def OnRender(self):
|
||||
_ui_events.append('renderclip')
|
||||
grp.SetViewport(0.0, 0.0, 0.05, 0.08)
|
||||
_ui_clip_probe = _ClipProbe()
|
||||
_ui_clip = wndMgr.Register(_ui_clip_probe, 'UI')
|
||||
wndMgr.SetWindowSize(_ui_clip, 800, 600)
|
||||
wndMgr.SetParent(_ui_bar, _ui_clip)
|
||||
wndMgr.Show(_ui_clip)
|
||||
"""
|
||||
_ck(Metin2PythonHost.run_line(clip_source) == "", "40250 裁剪窗口已创建")
|
||||
draws = Metin2PythonHost.ui_render_commands()
|
||||
_ck(Metin2PythonHost.run_line("assert 'renderclip' in _ui_events") == "", "父窗口 OnRender 已执行")
|
||||
var has_clip := false
|
||||
for draw in draws:
|
||||
if draw["kind"] == "bar" and draw["clip_x2"] == 40.0 and draw["clip_y2"] == 48.0:
|
||||
has_clip = true
|
||||
_ck(has_clip, "40250 viewport 裁剪范围传给 Godot Canvas")
|
||||
var surface := PythonUISurface.new()
|
||||
var segment: PackedVector2Array = surface._clip_line(Vector2(-10, 10), Vector2(50, 10), Rect2(0, 0, 30, 30))
|
||||
_ck(segment.size() == 2 and segment[0] == Vector2(0, 10) and segment[1] == Vector2(30, 10),
|
||||
"Godot Canvas 线段裁剪保留可见区间")
|
||||
surface.free()
|
||||
_ck(Metin2PythonHost.run_line("wndMgr.Destroy(_ui_bar)") == "", "Bar 窗口已销毁")
|
||||
_ck(Metin2PythonHost.run_line("grp.RestoreViewport(); wndMgr.Destroy(_ui_clip)") == "", "裁剪窗口已销毁")
|
||||
_ck(Metin2PythonHost.run_line("_ui_text_probe = _UIProbe(); _ui_text = wndMgr.RegisterTextLine(_ui_text_probe, 'UI'); wndMgr.SetWindowPosition(_ui_text, 70, 80); wndMgr.SetText(_ui_text, 'Hello'); wndMgr.SetFontColor(_ui_text, 1.0, 0.5, 0.0); wndMgr.Show(_ui_text)") == "",
|
||||
"40250 TextLine 已创建")
|
||||
draws = Metin2PythonHost.ui_render_commands()
|
||||
var has_text := false
|
||||
for draw in draws:
|
||||
if draw["kind"] == "text" and draw["text"] == "Hello" and draw["x1"] == 70.0 and draw["y1"] == 80.0:
|
||||
has_text = true
|
||||
_ck(has_text, "40250 TextLine 文本进入 Godot Canvas 命令队列")
|
||||
_ck(Metin2PythonHost.run_line("wndMgr.Destroy(_ui_text)") == "", "TextLine 已销毁")
|
||||
var image_path := "locale/en/ui/vkey/key_backspace.tga"
|
||||
if Metin2Pack.exists(image_path):
|
||||
var packed_texture: Texture2D = preload("res://ui/ui_assets.gd").load_tex(AssetRoot.path(), image_path)
|
||||
_ck(packed_texture != null and packed_texture.get_width() > 0,
|
||||
"40250 pack 图片可解码为 Godot 纹理")
|
||||
var image_source := "_ui_image_probe = _UIProbe(); _ui_image = wndMgr.RegisterImageBox(_ui_image_probe, 'UI'); wndMgr.LoadImage(_ui_image, '%s'); wndMgr.SetWindowPosition(_ui_image, 120, 130); wndMgr.Show(_ui_image)" % image_path
|
||||
_ck(Metin2PythonHost.run_line(image_source) == "", "40250 pack 图片已加载到 ImageBox")
|
||||
draws = Metin2PythonHost.ui_render_commands()
|
||||
var has_image := false
|
||||
for draw in draws:
|
||||
if draw["kind"] == "image" and String(draw["text"]).replace("\\", "/") == image_path and draw["x1"] == 120.0 and draw["y1"] == 130.0 and draw["x2"] > draw["x1"] and draw["y2"] > draw["y1"]:
|
||||
has_image = true
|
||||
_ck(has_image, "40250 ImageBox 图片进入 Godot Canvas 命令队列")
|
||||
_ck(Metin2PythonHost.run_line("wndMgr.Destroy(_ui_image)") == "", "ImageBox 已销毁")
|
||||
var sub_path := "d:/ymir work/ui/cursor/cursor.sub"
|
||||
if Metin2Pack.exists(sub_path):
|
||||
_ck(Metin2PythonHost.run_line("import grpImage; _ui_sub = grpImage.Generate('%s'); assert _ui_sub; grpImage.Delete(_ui_sub)" % sub_path) == "",
|
||||
"40250 .sub 子图可创建并释放")
|
||||
_ck(Metin2PythonHost.run_line("_ui_expanded = grpImage.GenerateExpanded('%s'); assert _ui_expanded; grpImage.DeleteExpanded(_ui_expanded)" % image_path) == "",
|
||||
"40250 ExpandedImage 可创建并释放")
|
||||
else:
|
||||
print("SKIP: 40250 image test(pack 缺少 %s)" % image_path)
|
||||
_ck(Metin2PythonHost.run_line("wndMgr.Destroy(_ui_window)") == "", "窗口已销毁")
|
||||
else:
|
||||
print("SKIP: system.py 这一段(没有 40250 Client/pack:%s)" % AssetRoot.client_path())
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# Canvas adapter for the 40250 CWindowManager. The window tree remains native;
|
||||
# this Control forwards Godot input and draws the native UI command stream.
|
||||
extends Control
|
||||
|
||||
const AssetRoot = preload("res://asset_root.gd")
|
||||
const UiAssets = preload("res://ui/ui_assets.gd")
|
||||
|
||||
const KEY_TO_DIK := {
|
||||
KEY_ESCAPE: 0x01, KEY_1: 0x02, KEY_2: 0x03, KEY_3: 0x04,
|
||||
KEY_4: 0x05, KEY_5: 0x06, KEY_6: 0x07, KEY_7: 0x08,
|
||||
KEY_8: 0x09, KEY_9: 0x0a, KEY_0: 0x0b, KEY_BACKSPACE: 0x0e,
|
||||
KEY_TAB: 0x0f, KEY_Q: 0x10, KEY_W: 0x11, KEY_E: 0x12,
|
||||
KEY_R: 0x13, KEY_T: 0x14, KEY_Y: 0x15, KEY_U: 0x16,
|
||||
KEY_I: 0x17, KEY_O: 0x18, KEY_P: 0x19, KEY_ENTER: 0x1c,
|
||||
KEY_A: 0x1e, KEY_S: 0x1f, KEY_D: 0x20, KEY_F: 0x21,
|
||||
KEY_G: 0x22, KEY_H: 0x23, KEY_J: 0x24, KEY_K: 0x25,
|
||||
KEY_L: 0x26, KEY_Z: 0x2c, KEY_X: 0x2d, KEY_C: 0x2e,
|
||||
KEY_V: 0x2f, KEY_B: 0x30, KEY_N: 0x31, KEY_M: 0x32,
|
||||
KEY_SPACE: 0x39, KEY_UP: 0xc8, KEY_LEFT: 0xcb,
|
||||
KEY_RIGHT: 0xcd, KEY_DOWN: 0xd0,
|
||||
}
|
||||
|
||||
func _ready() -> void:
|
||||
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_sync_size()
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_RESIZED and is_inside_tree():
|
||||
_sync_size()
|
||||
|
||||
func _sync_size() -> void:
|
||||
if ClassDB.class_exists("Metin2PythonHost"):
|
||||
Metin2PythonHost.set_ui_size(int(size.x), int(size.y))
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Metin2PythonHost.is_running():
|
||||
Metin2PythonHost.ui_update()
|
||||
queue_redraw()
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if not Metin2PythonHost.is_running():
|
||||
return
|
||||
if event is InputEventMouseMotion:
|
||||
Metin2PythonHost.ui_mouse_move(int(event.position.x), int(event.position.y))
|
||||
elif event is InputEventMouseButton:
|
||||
if event.button_index >= MOUSE_BUTTON_LEFT and event.button_index <= MOUSE_BUTTON_MIDDLE:
|
||||
Metin2PythonHost.ui_mouse_button(event.button_index, event.pressed,
|
||||
int(event.position.x), int(event.position.y))
|
||||
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and Metin2PythonHost.is_running():
|
||||
var dik: int = KEY_TO_DIK.get(event.physical_keycode, 0)
|
||||
if dik != 0:
|
||||
Metin2PythonHost.ui_key(dik, event.pressed)
|
||||
|
||||
func _draw() -> void:
|
||||
if not Metin2PythonHost.is_running():
|
||||
return
|
||||
for command in Metin2PythonHost.ui_render_commands():
|
||||
var argb: int = command["argb"]
|
||||
var color := Color8((argb >> 16) & 255, (argb >> 8) & 255,
|
||||
argb & 255, (argb >> 24) & 255)
|
||||
var p1 := Vector2(command["x1"], command["y1"])
|
||||
var p2 := Vector2(command["x2"], command["y2"])
|
||||
var clip := Rect2(Vector2(command["clip_x1"], command["clip_y1"]),
|
||||
Vector2(command["clip_x2"] - command["clip_x1"],
|
||||
command["clip_y2"] - command["clip_y1"]))
|
||||
if command["kind"] == "bar":
|
||||
var visible := Rect2(p1, p2 - p1).intersection(clip)
|
||||
if visible.has_area():
|
||||
draw_rect(visible, color, true)
|
||||
elif command["kind"] == "line":
|
||||
var segment := _clip_line(p1, p2, clip)
|
||||
if segment.size() == 2:
|
||||
draw_line(segment[0], segment[1], color)
|
||||
elif command["kind"] == "text":
|
||||
var font: Font = ThemeDB.fallback_font
|
||||
if font != null:
|
||||
var baseline := p1 + Vector2(0, font.get_ascent(14))
|
||||
if clip.has_point(p1):
|
||||
draw_string(font, baseline, command["text"], HORIZONTAL_ALIGNMENT_LEFT,
|
||||
clip.end.x - p1.x, 14, color)
|
||||
elif command["kind"] == "image":
|
||||
var texture: Texture2D = UiAssets.load_tex(AssetRoot.path(), command["text"])
|
||||
if texture != null:
|
||||
var dest := Rect2(p1, p2 - p1)
|
||||
var visible := dest.intersection(clip)
|
||||
if visible.has_area() and dest.has_area():
|
||||
var scale := texture.get_size() / dest.size
|
||||
var source := Rect2((visible.position - dest.position) * scale, visible.size * scale)
|
||||
draw_texture_rect_region(texture, visible, source, color)
|
||||
|
||||
func _clip_line(a: Vector2, b: Vector2, clip: Rect2) -> PackedVector2Array:
|
||||
var delta := b - a
|
||||
var p := [-delta.x, delta.x, -delta.y, delta.y]
|
||||
var q := [a.x - clip.position.x, clip.end.x - a.x,
|
||||
a.y - clip.position.y, clip.end.y - a.y]
|
||||
var enter := 0.0
|
||||
var leave := 1.0
|
||||
for i in range(4):
|
||||
if is_zero_approx(p[i]):
|
||||
if q[i] < 0.0:
|
||||
return PackedVector2Array()
|
||||
else:
|
||||
var t: float = q[i] / p[i]
|
||||
if p[i] < 0.0:
|
||||
enter = maxf(enter, t)
|
||||
else:
|
||||
leave = minf(leave, t)
|
||||
if enter > leave:
|
||||
return PackedVector2Array()
|
||||
return PackedVector2Array([a + delta * enter, a + delta * leave])
|
||||
+27
-1
@@ -67,11 +67,37 @@ static func _load_uncached(assets_root: String, vpath: String) -> Texture2D:
|
||||
if real != "":
|
||||
break
|
||||
if real == "":
|
||||
return null
|
||||
return _load_pack_image(rel)
|
||||
if real.get_extension().to_lower() == "sub":
|
||||
return _load_sub(real)
|
||||
return _load_image_file(real)
|
||||
|
||||
static func _load_pack_image(rel: String) -> Texture2D:
|
||||
if not ClassDB.class_exists("Metin2Pack") or not Metin2Pack.is_ready():
|
||||
return null
|
||||
if not Metin2Pack.exists(rel):
|
||||
return null
|
||||
var ext := rel.get_extension().to_lower()
|
||||
var image: Image
|
||||
if ext == "dds":
|
||||
if not ClassDB.class_exists("Metin2World"):
|
||||
return null
|
||||
var helper: Object = ClassDB.instantiate("Metin2World")
|
||||
image = helper.call("load_dds", "pack://" + rel)
|
||||
helper.free()
|
||||
else:
|
||||
var bytes: PackedByteArray = Metin2Pack.get_bytes(rel)
|
||||
image = Image.new()
|
||||
var err := ERR_FILE_UNRECOGNIZED
|
||||
match ext:
|
||||
"tga": err = image.load_tga_from_buffer(bytes)
|
||||
"png": err = image.load_png_from_buffer(bytes)
|
||||
"jpg", "jpeg": err = image.load_jpg_from_buffer(bytes)
|
||||
"bmp": err = image.load_bmp_from_buffer(bytes)
|
||||
if err != OK:
|
||||
return null
|
||||
return ImageTexture.create_from_image(image) if image != null and not image.is_empty() else null
|
||||
|
||||
static func _load_image_file(path: String) -> Texture2D:
|
||||
# .sub 文件可能引用了未随当前资源包发布的共享贴图(例如 Public.tga)。
|
||||
# 先做存在性检查,避免 headless/UI fallback 因缺失可选贴图刷错误日志。
|
||||
|
||||
Reference in New Issue
Block a user