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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
53d06c3c26
commit
3e3708ef06
@@ -5,6 +5,7 @@
|
||||
#include "ScriptLib/Resource.h"
|
||||
#include "UserInterface/StdAfx.h" // initpack and the rest of the module initializer list
|
||||
#include "EterPythonLib/StdAfx.h"
|
||||
#include "EterPythonLib/PythonWindow.h"
|
||||
#include "EterBase/Timer.h"
|
||||
#include "EterLib/Util.h"
|
||||
#include "EterLib/GrpBase.h"
|
||||
@@ -151,6 +152,7 @@ struct GameSingletons
|
||||
};
|
||||
std::unique_ptr<GameSingletons> g_game_singletons;
|
||||
bool g_host_hardware_cursor_enabled = false;
|
||||
bool g_touch_input = false;
|
||||
|
||||
bool fail(std::string* error, const std::string& text)
|
||||
{
|
||||
@@ -268,11 +270,20 @@ bool IsRunning()
|
||||
return g_launcher != nullptr;
|
||||
}
|
||||
|
||||
static int g_ui_safe_inset = 0;
|
||||
|
||||
void SetUISafeInset(int inset)
|
||||
{
|
||||
g_ui_safe_inset = std::max(0, inset);
|
||||
}
|
||||
|
||||
void SetUISize(int width, int height)
|
||||
{
|
||||
if (!g_window_manager || width <= 0 || height <= 0) return;
|
||||
g_window_manager->SetScreenSize(width, height);
|
||||
const int inset = std::min(g_ui_safe_inset, width / 8);
|
||||
g_window_manager->SetScreenSize(width - 2 * inset, height);
|
||||
g_window_manager->SetResolution(width, height);
|
||||
g_window_manager->SetScreenOffsetX(inset);
|
||||
UIRenderSetSize(width, height);
|
||||
CGraphicBase::SetBackBufferSize(width, height);
|
||||
if (g_game_singletons)
|
||||
@@ -290,8 +301,10 @@ void update_3d_pick_ray()
|
||||
return;
|
||||
long lx = 0, ly = 0;
|
||||
g_window_manager->GetMousePosition(lx, ly);
|
||||
const long sw = g_window_manager->GetScreenWidth();
|
||||
const long sh = g_window_manager->GetScreenHeight();
|
||||
// The pick ray goes through the full-screen 3D view: screen coordinates and resolution.
|
||||
lx += g_window_manager->GetScreenOffsetX();
|
||||
long sw = 0, sh = 0;
|
||||
g_window_manager->GetResolution(sw, sh);
|
||||
if (sw <= 0 || sh <= 0)
|
||||
return;
|
||||
CScreen s;
|
||||
@@ -330,6 +343,11 @@ void UIMouseButton(int button, bool pressed, int x, int y)
|
||||
CPythonApplication::Instance().OnMouseMove(x, y);
|
||||
else
|
||||
g_window_manager->RunMouseMove(x, y);
|
||||
{
|
||||
long lx = x, ly = y;
|
||||
g_window_manager->GetMousePosition(lx, ly);
|
||||
MtHostSetCursor(static_cast<int>(lx), static_cast<int>(ly));
|
||||
}
|
||||
update_3d_pick_ray();
|
||||
switch (button)
|
||||
{
|
||||
@@ -398,7 +416,8 @@ void SetMoveDirection(float angleDeg, bool moving)
|
||||
{
|
||||
if (!g_game_singletons) return;
|
||||
if (moving)
|
||||
CPythonPlayer::Instance().NEW_MoveToDirection(angleDeg);
|
||||
// The touch stick only steers the player; the camera turns by the right-hand drag.
|
||||
CPythonPlayer::Instance().NEW_MoveToDirection(angleDeg, false);
|
||||
else
|
||||
CPythonPlayer::Instance().NEW_Stop();
|
||||
}
|
||||
@@ -415,6 +434,30 @@ void UseLocalQuickSlot(int localSlotIndex)
|
||||
CPythonPlayer::Instance().RequestUseLocalQuickSlot(static_cast<DWORD>(localSlotIndex));
|
||||
}
|
||||
|
||||
void AutoHuntToggle()
|
||||
{
|
||||
std::string error;
|
||||
if (g_game_singletons)
|
||||
RunLine("__import__('mt_autohunt').Toggle()", &error);
|
||||
}
|
||||
|
||||
void AutoHuntOpenSettings()
|
||||
{
|
||||
std::string error;
|
||||
if (g_game_singletons)
|
||||
RunLine("__import__('mt_autohunt').OpenSettings()", &error);
|
||||
}
|
||||
|
||||
bool AutoHuntIsEnabled()
|
||||
{
|
||||
return g_game_singletons && CPythonPlayer::Instance().AutoHunt_IsEnabled();
|
||||
}
|
||||
|
||||
bool AutoHuntAvailable()
|
||||
{
|
||||
return g_game_singletons && CPythonPlayer::Instance().NEW_GetMainActorPtr() != nullptr;
|
||||
}
|
||||
|
||||
bool TryAssignAttachedObjectToLocalQuickSlot(int localSlotIndex)
|
||||
{
|
||||
if (!g_game_singletons || localSlotIndex < 0 || localSlotIndex >= 8) return false;
|
||||
@@ -429,55 +472,130 @@ bool TryAssignAttachedObjectToLocalQuickSlot(int localSlotIndex)
|
||||
return RunLine(command.c_str(), &error);
|
||||
}
|
||||
|
||||
std::string LocalQuickSlotSkillIcon(int localSlotIndex)
|
||||
namespace
|
||||
{
|
||||
if (!g_game_singletons || localSlotIndex < 0 || localSlotIndex >= 8) return {};
|
||||
// The icons are .sub images (a rect of a shared texture). A resource only loads while referenced, and
|
||||
// on touch hosts the taskbar slots that would reference it are hidden, so the overlay holds one.
|
||||
bool quick_slot_image_uv(CGraphicImage* image, std::string* texture, float uv[4])
|
||||
{
|
||||
if (!image) return false;
|
||||
static std::vector<CGraphicImage::TRef> s_heldIcons;
|
||||
if (std::none_of(s_heldIcons.begin(), s_heldIcons.end(),
|
||||
[image](const CGraphicImage::TRef& ref) { return ref.GetPointer() == image; }))
|
||||
s_heldIcons.emplace_back(image);
|
||||
|
||||
CGraphicTexture* tex = image->GetTexturePointer();
|
||||
if (image->IsEmpty() || !tex || tex->GetWidth() <= 0 || tex->GetHeight() <= 0) return false;
|
||||
*texture = UIRenderTextureName(tex);
|
||||
if (texture->empty()) return false;
|
||||
// CGraphicImageInstance::OnRender's texture coordinates.
|
||||
const RECT& rc = image->GetRectReference();
|
||||
uv[0] = float(rc.left) / float(tex->GetWidth());
|
||||
uv[1] = float(rc.top) / float(tex->GetHeight());
|
||||
uv[2] = float(rc.right) / float(tex->GetWidth());
|
||||
uv[3] = float(rc.bottom) / float(tex->GetHeight());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalQuickSlotIcon(int localSlotIndex, std::string* texture, float uv[4], int* count)
|
||||
{
|
||||
if (!g_game_singletons || localSlotIndex < 0 || localSlotIndex >= 8) return false;
|
||||
DWORD type = SLOT_TYPE_NONE;
|
||||
DWORD position = 0;
|
||||
CPythonPlayer& player = CPythonPlayer::Instance();
|
||||
player.GetLocalQuickSlotData(static_cast<DWORD>(localSlotIndex), &type, &position);
|
||||
if (type != SLOT_TYPE_SKILL) return {};
|
||||
*count = 0;
|
||||
|
||||
CPythonSkill::TSkillData* skillData = nullptr;
|
||||
if (!CPythonSkill::Instance().GetSkillData(player.GetSkillIndex(position), &skillData) || !skillData)
|
||||
return {};
|
||||
const int grade = std::clamp(player.GetSkillGrade(position), 0, CPythonSkill::SKILL_GRADE_COUNT - 1);
|
||||
CGraphicImage* image = skillData->GradeData[grade].pImage;
|
||||
if (!image) image = skillData->pImage;
|
||||
return image ? image->GetFileName() : std::string{};
|
||||
}
|
||||
|
||||
void CameraBeginDrag(int x, int y)
|
||||
{
|
||||
CCameraManager& rkCmrMgr = CCameraManager::Instance();
|
||||
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
|
||||
if (pkCmrCur)
|
||||
pkCmrCur->BeginDrag(x, y);
|
||||
}
|
||||
|
||||
void CameraDrag(int x, int y)
|
||||
{
|
||||
CCameraManager& rkCmrMgr = CCameraManager::Instance();
|
||||
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
|
||||
if (pkCmrCur)
|
||||
{
|
||||
POINT pt;
|
||||
pkCmrCur->Drag(x, y, &pt);
|
||||
if (type == SLOT_TYPE_SKILL) {
|
||||
CPythonSkill::TSkillData* skillData = nullptr;
|
||||
if (!CPythonSkill::Instance().GetSkillData(player.GetSkillIndex(position), &skillData) || !skillData)
|
||||
return false;
|
||||
const int grade = std::clamp(player.GetSkillGrade(position), 0, CPythonSkill::SKILL_GRADE_COUNT - 1);
|
||||
CGraphicImage* image = skillData->GradeData[grade].pImage;
|
||||
if (!image) image = skillData->pImage;
|
||||
return quick_slot_image_uv(image, texture, uv);
|
||||
}
|
||||
if (type == SLOT_TYPE_INVENTORY) {
|
||||
// uitaskbar.RefreshQuickSlot: the item's icon and, above one, its count.
|
||||
const TItemPos cell(INVENTORY, static_cast<WORD>(position));
|
||||
CItemData* itemData = nullptr;
|
||||
if (!CItemManager::Instance().GetItemDataPointer(player.GetItemIndex(cell), &itemData) || !itemData)
|
||||
return false;
|
||||
const DWORD itemCount = player.GetItemCount(cell);
|
||||
*count = itemCount > 1 ? static_cast<int>(itemCount) : 0;
|
||||
return quick_slot_image_uv(itemData->GetIconImage(), texture, uv);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CameraEndDrag()
|
||||
void UIMouseDoubleClick(int x, int y)
|
||||
{
|
||||
CCameraManager& rkCmrMgr = CCameraManager::Instance();
|
||||
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
|
||||
if (pkCmrCur)
|
||||
pkCmrCur->EndDrag();
|
||||
MtHostSetCursor(x, y);
|
||||
if (!g_window_manager) return;
|
||||
if (g_game_singletons)
|
||||
CPythonApplication::Instance().OnMouseMove(x, y);
|
||||
else
|
||||
g_window_manager->RunMouseMove(x, y);
|
||||
update_3d_pick_ray();
|
||||
g_window_manager->RunMouseLeftButtonDoubleClick(x, y);
|
||||
}
|
||||
|
||||
bool UIIsAttaching()
|
||||
{
|
||||
return g_window_manager && g_window_manager->IsAttaching();
|
||||
}
|
||||
|
||||
void UIDeattachObject()
|
||||
{
|
||||
std::string error;
|
||||
RunLine("__import__('mouseModule').mouseController.DeattachObject()", &error);
|
||||
}
|
||||
|
||||
void CameraRotateBy(float rollDeg, float pitchDeg)
|
||||
{
|
||||
CCamera* pkCmrCur = CCameraManager::Instance().GetCurrentCamera();
|
||||
if (!pkCmrCur || pkCmrCur->IsLock())
|
||||
return;
|
||||
|
||||
// Same collision limits CCamera::Drag applies to its velocities.
|
||||
switch (pkCmrCur->GetCameraState())
|
||||
{
|
||||
case CAMERA_STATE_CANTGOLEFT: rollDeg = std::max(0.0f, rollDeg); break;
|
||||
case CAMERA_STATE_CANTGORIGHT: rollDeg = std::min(0.0f, rollDeg); break;
|
||||
case CAMERA_STATE_CANTGODOWN: pitchDeg = std::max(0.0f, pitchDeg); break;
|
||||
default: break;
|
||||
}
|
||||
pkCmrCur->RotateEyeAroundTarget(pitchDeg, rollDeg);
|
||||
}
|
||||
|
||||
void SetTouchInput(bool touch)
|
||||
{
|
||||
g_touch_input = touch;
|
||||
UI::CWindow::SetHideVirtualKeyboard(touch);
|
||||
}
|
||||
|
||||
bool TouchInput()
|
||||
{
|
||||
return g_touch_input;
|
||||
}
|
||||
|
||||
bool TextInputFocused()
|
||||
{
|
||||
return g_window_manager && CIME::ms_bCaptureInput;
|
||||
}
|
||||
|
||||
bool IsPointInsideFocusedWindow(int x, int y)
|
||||
{
|
||||
if (!g_window_manager) return false;
|
||||
UI::CWindow* pWin = g_window_manager->GetActivateWindow();
|
||||
return pWin && pWin->IsShow() && pWin->IsIn(x - g_window_manager->GetScreenOffsetX(), y);
|
||||
}
|
||||
|
||||
bool IsPointInsideActiveUI(int x, int y)
|
||||
{
|
||||
if (!g_window_manager) return false;
|
||||
return g_window_manager->IsPointInsideActiveUI(x, y);
|
||||
return g_window_manager->IsPointInsideActiveUI(x - g_window_manager->GetScreenOffsetX(), y);
|
||||
}
|
||||
|
||||
PlayerStatusInfo GetPlayerStatusInfo()
|
||||
@@ -745,6 +863,32 @@ bool run_main_script_body(const char* lpCmdLine, std::string* error)
|
||||
namespace PythonBoot
|
||||
{
|
||||
|
||||
// PORT: the auto hunt window (AutoHuntScript.inc) as module mt_autohunt and the account registration
|
||||
// window (RegisterScript.inc) as mt_register. They hook game.GameWindow / introLogin.LoginWindow, so they
|
||||
// have to run once the root scripts are importable; a failure leaves the game without them, not broken.
|
||||
#include "AutoHuntScript.inc"
|
||||
#include "RegisterScript.inc"
|
||||
|
||||
static void install_script_module(const char* c_szModule, const char* c_szScript)
|
||||
{
|
||||
PyObject* module = PyImport_AddModule(c_szModule);
|
||||
PyObject* dict = module ? PyModule_GetDict(module) : nullptr;
|
||||
if (!dict)
|
||||
{
|
||||
PyErr_Clear();
|
||||
return;
|
||||
}
|
||||
if (!PyDict_GetItemString(dict, "__builtins__"))
|
||||
PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
|
||||
PyObject* result = PyRun_String(c_szScript, Py_file_input, dict, dict);
|
||||
if (!result)
|
||||
{
|
||||
TraceError("%s: install failed", c_szModule);
|
||||
PyErr_Print();
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
}
|
||||
|
||||
bool RunMainScript(const char* lpCmdLine, std::string* error)
|
||||
{
|
||||
if (!g_launcher)
|
||||
@@ -775,6 +919,11 @@ bool RunMainScript(const char* lpCmdLine, std::string* error)
|
||||
if (!g_fiber.ok)
|
||||
return fail(error, g_fiber.error);
|
||||
}
|
||||
if (IsAppLooping())
|
||||
{
|
||||
install_script_module("mt_autohunt", c_szAutoHuntScript);
|
||||
install_script_module("mt_register", c_szRegisterScript);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user