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
@@ -0,0 +1,385 @@
|
||||
// The auto hunt window (PORT; 40250 has no counterpart), run by PythonBoot::RunMainScript as module
|
||||
// mt_autohunt. It hooks game.GameWindow.Open/Close so the window lives exactly as long as the game phase,
|
||||
// whichever way the host reached it. The logic is CPythonPlayer's (UserInterface/PythonPlayerAutoHunt.cpp);
|
||||
// this is only the player.AutoHunt* front end. Strings are UTF-8 here and go through
|
||||
// player.AutoHuntLocalize into the locale's code page, as the packed locale strings are.
|
||||
static const char c_szAutoHuntScript[] = R"PY(# -*- coding: utf-8 -*-
|
||||
import app
|
||||
import chat
|
||||
import game
|
||||
import player
|
||||
import ui
|
||||
import wndMgr
|
||||
|
||||
L = player.AutoHuntLocalize
|
||||
|
||||
STATE_OFF, STATE_SEARCH, STATE_ENGAGE, STATE_LOOT, STATE_RETURN, STATE_PAUSED = range(6)
|
||||
STOP_NONE, STOP_USER, STOP_DEAD, STOP_MAP, STOP_INVENTORY_FULL = range(5)
|
||||
SKILL_SLOT_NUM = 8
|
||||
|
||||
STATE_TEXT = {
|
||||
STATE_SEARCH: "寻找目标",
|
||||
STATE_ENGAGE: "战斗中",
|
||||
STATE_LOOT: "拾取中",
|
||||
STATE_RETURN: "返回挂机点",
|
||||
STATE_PAUSED: "手动操作,3秒后继续",
|
||||
}
|
||||
STOP_TEXT = {
|
||||
STOP_USER: "自动狩猎已停止",
|
||||
STOP_DEAD: "角色死亡,自动狩猎已停止",
|
||||
STOP_MAP: "切换地图,自动狩猎已停止",
|
||||
STOP_INVENTORY_FULL: "背包已满,自动狩猎已停止",
|
||||
}
|
||||
|
||||
RANGE_VALUES = (1000, 2000, 3500)
|
||||
RANGE_TEXTS = ("近", "中", "远")
|
||||
HP_VALUES = (0, 30, 50, 70)
|
||||
SP_VALUES = (0, 20, 40, 60)
|
||||
PICKUP_TEXTS = ("不拾取", "全部", "金币装备")
|
||||
GATHER_VALUES = (0, 3, 5, 8)
|
||||
GATHER_TEXTS = ("关", "3只", "5只", "8只")
|
||||
|
||||
SMALL_BUTTON = "d:/ymir work/ui/public/small_button_%02d.sub"
|
||||
MIDDLE_BUTTON = "d:/ymir work/ui/public/middle_button_%02d.sub"
|
||||
XSMALL_BUTTON = "d:/ymir work/ui/public/xsmall_button_%02d.sub"
|
||||
|
||||
COLOR_ON = 0xff7fff7f
|
||||
COLOR_OFF = 0xffffcf7f
|
||||
COLOR_SELECTED = 0xffffd24a
|
||||
COLOR_UNSELECTED = 0xffc8c8c8
|
||||
|
||||
|
||||
def _MarkButton(button, isSelected):
|
||||
"Down and gold text while selected: the down visual alone is hard to tell apart on a phone."
|
||||
if isSelected:
|
||||
button.Down()
|
||||
else:
|
||||
button.SetUp()
|
||||
if button.ButtonText:
|
||||
button.ButtonText.SetPackedFontColor(isSelected and COLOR_SELECTED or COLOR_UNSELECTED)
|
||||
|
||||
|
||||
def _Nearest(values, value):
|
||||
best = 0
|
||||
for i in xrange(len(values)):
|
||||
if abs(values[i] - value) < abs(values[best] - value):
|
||||
best = i
|
||||
return best
|
||||
|
||||
|
||||
class ChoiceRow:
|
||||
"One setting: a label and a row of buttons of which exactly one stays down."
|
||||
|
||||
def __init__(self, parent, y, label, texts, image, step, event):
|
||||
self.label = ui.TextLine()
|
||||
self.label.SetParent(parent)
|
||||
self.label.SetPosition(18, y + 4)
|
||||
self.label.SetText(L(label))
|
||||
self.label.Show()
|
||||
|
||||
self.buttons = []
|
||||
self.event = event
|
||||
self.index = -1
|
||||
x = 70
|
||||
for i in xrange(len(texts)):
|
||||
button = ui.RadioButton()
|
||||
button.SetParent(parent)
|
||||
button.SetUpVisual(image % 1)
|
||||
button.SetOverVisual(image % 2)
|
||||
button.SetDownVisual(image % 3)
|
||||
button.SetPosition(x, y)
|
||||
button.SetText(L(texts[i]))
|
||||
button.SetEvent(lambda i=i: self.Select(i, True))
|
||||
button.Show()
|
||||
self.buttons.append(button)
|
||||
x += step
|
||||
|
||||
def Select(self, index, notify):
|
||||
self.index = index
|
||||
for i in xrange(len(self.buttons)):
|
||||
_MarkButton(self.buttons[i], i == index)
|
||||
if notify and self.event:
|
||||
self.event()
|
||||
|
||||
def Destroy(self):
|
||||
self.event = None
|
||||
self.buttons = []
|
||||
self.label = None
|
||||
|
||||
|
||||
class FlagButton(ui.ToggleButton):
|
||||
"A button that stays down while its flag is on."
|
||||
|
||||
def __init__(self, parent, x, y, image, text, event):
|
||||
ui.ToggleButton.__init__(self)
|
||||
self.SetParent(parent)
|
||||
self.SetUpVisual(image % 1)
|
||||
self.SetOverVisual(image % 2)
|
||||
self.SetDownVisual(image % 3)
|
||||
self.SetPosition(x, y)
|
||||
self.SetText(L(text))
|
||||
self.flag = False
|
||||
self.flagEvent = event
|
||||
self.SetToggleDownEvent(lambda: self.__OnToggle(True))
|
||||
self.SetToggleUpEvent(lambda: self.__OnToggle(False))
|
||||
self.Show()
|
||||
|
||||
def __OnToggle(self, flag):
|
||||
self.SetFlag(flag)
|
||||
if self.flagEvent:
|
||||
self.flagEvent()
|
||||
|
||||
def SetFlag(self, flag):
|
||||
self.flag = flag
|
||||
_MarkButton(self, flag)
|
||||
|
||||
def Destroy(self):
|
||||
self.flagEvent = None
|
||||
self.eventUp = None
|
||||
self.eventDown = None
|
||||
|
||||
|
||||
class SettingDialog(ui.BoardWithTitleBar):
|
||||
WIDTH = 330
|
||||
HEIGHT = 300
|
||||
|
||||
def __init__(self):
|
||||
ui.BoardWithTitleBar.__init__(self)
|
||||
self.AddFlag("movable")
|
||||
self.AddFlag("float")
|
||||
self.SetSize(self.WIDTH, self.HEIGHT)
|
||||
self.SetTitleName(L("自动狩猎"))
|
||||
self.SetCloseEvent(self.Close)
|
||||
|
||||
y = 36
|
||||
self.rangeRow = ChoiceRow(self, y, "范围", RANGE_TEXTS, SMALL_BUTTON, 48, self.__Save)
|
||||
y += 28
|
||||
self.hpRow = ChoiceRow(self, y, "红药", ["关"] + ["%d%%" % v for v in HP_VALUES[1:]], SMALL_BUTTON, 48, self.__Save)
|
||||
y += 28
|
||||
self.spRow = ChoiceRow(self, y, "蓝药", ["关"] + ["%d%%" % v for v in SP_VALUES[1:]], SMALL_BUTTON, 48, self.__Save)
|
||||
y += 28
|
||||
self.pickupRow = ChoiceRow(self, y, "拾取", PICKUP_TEXTS, MIDDLE_BUTTON, 66, self.__Save)
|
||||
y += 28
|
||||
self.gatherRow = ChoiceRow(self, y, "聚怪", GATHER_TEXTS, SMALL_BUTTON, 48, self.__Save)
|
||||
y += 28
|
||||
|
||||
self.skillLabel = ui.TextLine()
|
||||
self.skillLabel.SetParent(self)
|
||||
self.skillLabel.SetPosition(18, y + 4)
|
||||
self.skillLabel.SetText(L("技能栏"))
|
||||
self.skillLabel.Show()
|
||||
self.skillButtons = []
|
||||
for i in xrange(SKILL_SLOT_NUM):
|
||||
self.skillButtons.append(FlagButton(self, 70 + i * 31, y, XSMALL_BUTTON, str(i + 1), self.__Save))
|
||||
y += 28
|
||||
|
||||
self.targetLabel = ui.TextLine()
|
||||
self.targetLabel.SetParent(self)
|
||||
self.targetLabel.SetPosition(18, y + 4)
|
||||
self.targetLabel.SetText(L("攻击"))
|
||||
self.targetLabel.Show()
|
||||
self.stoneButton = FlagButton(self, 70, y, MIDDLE_BUTTON, "石头", self.__Save)
|
||||
self.bossButton = FlagButton(self, 136, y, MIDDLE_BUTTON, "首领", self.__Save)
|
||||
y += 30
|
||||
|
||||
self.hintLine = ui.TextLine()
|
||||
self.hintLine.SetParent(self)
|
||||
self.hintLine.SetPosition(0, y + 2)
|
||||
self.hintLine.SetWindowHorizontalAlignCenter()
|
||||
self.hintLine.SetHorizontalAlignCenter()
|
||||
self.hintLine.SetPackedFontColor(0xffa0a0a0)
|
||||
self.hintLine.SetText(L("手动操作时暂停,3秒后继续"))
|
||||
self.hintLine.Show()
|
||||
y += 26
|
||||
|
||||
self.startButton = ui.Button()
|
||||
self.startButton.SetParent(self)
|
||||
self.startButton.SetUpVisual("d:/ymir work/ui/public/large_button_01.sub")
|
||||
self.startButton.SetOverVisual("d:/ymir work/ui/public/large_button_02.sub")
|
||||
self.startButton.SetDownVisual("d:/ymir work/ui/public/large_button_03.sub")
|
||||
self.startButton.SetPosition(0, y)
|
||||
self.startButton.SetWindowHorizontalAlignCenter()
|
||||
self.startButton.SetEvent(self.__OnClickStart)
|
||||
self.startButton.Show()
|
||||
|
||||
self.SetSize(self.WIDTH, y + 36)
|
||||
self.SetCenterPosition()
|
||||
self.isStarted = None
|
||||
|
||||
def Destroy(self):
|
||||
for row in (self.rangeRow, self.hpRow, self.spRow, self.pickupRow, self.gatherRow):
|
||||
row.Destroy()
|
||||
for button in self.skillButtons + [self.stoneButton, self.bossButton]:
|
||||
button.Destroy()
|
||||
self.skillButtons = []
|
||||
self.startButton = None
|
||||
self.Hide()
|
||||
|
||||
def Open(self):
|
||||
(range_, hp, sp, pickup, stone, boss, mask, gather) = player.AutoHuntGetConfig()
|
||||
self.rangeRow.Select(_Nearest(RANGE_VALUES, range_), False)
|
||||
self.hpRow.Select(_Nearest(HP_VALUES, hp), False)
|
||||
self.spRow.Select(_Nearest(SP_VALUES, sp), False)
|
||||
self.pickupRow.Select(pickup, False)
|
||||
self.gatherRow.Select(_Nearest(GATHER_VALUES, gather), False)
|
||||
for i in xrange(SKILL_SLOT_NUM):
|
||||
self.skillButtons[i].SetFlag(bool(mask & (1 << i)))
|
||||
self.stoneButton.SetFlag(bool(stone))
|
||||
self.bossButton.SetFlag(bool(boss))
|
||||
self.isStarted = None
|
||||
self.Show()
|
||||
self.SetTop()
|
||||
|
||||
def Close(self):
|
||||
self.Hide()
|
||||
|
||||
def OnPressEscapeKey(self):
|
||||
self.Close()
|
||||
return True
|
||||
|
||||
def OnUpdate(self):
|
||||
isStarted = bool(player.AutoHuntIsEnabled())
|
||||
if isStarted != self.isStarted:
|
||||
self.isStarted = isStarted
|
||||
self.startButton.SetText(L(isStarted and "停止狩猎" or "开始狩猎"))
|
||||
|
||||
def __Save(self):
|
||||
mask = 0
|
||||
for i in xrange(SKILL_SLOT_NUM):
|
||||
if self.skillButtons[i].flag:
|
||||
mask |= 1 << i
|
||||
player.AutoHuntSetConfig(RANGE_VALUES[self.rangeRow.index], HP_VALUES[self.hpRow.index],
|
||||
SP_VALUES[self.spRow.index], self.pickupRow.index, int(self.stoneButton.flag), int(self.bossButton.flag), mask,
|
||||
GATHER_VALUES[self.gatherRow.index])
|
||||
|
||||
def __OnClickStart(self):
|
||||
Toggle()
|
||||
if player.AutoHuntIsEnabled():
|
||||
self.Close()
|
||||
|
||||
|
||||
class StatusBoard(ui.ThinBoard):
|
||||
def __init__(self):
|
||||
ui.ThinBoard.__init__(self)
|
||||
self.AddFlag("not_pick")
|
||||
self.SetSize(260, 28)
|
||||
self.SetWindowHorizontalAlignCenter()
|
||||
self.SetPosition(0, 64)
|
||||
self.textLine = ui.TextLine()
|
||||
self.textLine.SetParent(self)
|
||||
self.textLine.SetPosition(0, 7)
|
||||
self.textLine.SetWindowHorizontalAlignCenter()
|
||||
self.textLine.SetHorizontalAlignCenter()
|
||||
self.textLine.Show()
|
||||
|
||||
def SetStatus(self, text, color):
|
||||
self.textLine.SetText(text)
|
||||
self.textLine.SetPackedFontColor(color)
|
||||
self.Show()
|
||||
|
||||
|
||||
class Ticker(ui.Window):
|
||||
"Always shown and empty: polls the hunt state for the status board, which is hidden while off."
|
||||
|
||||
def __init__(self, status):
|
||||
ui.Window.__init__(self)
|
||||
self.AddFlag("not_pick")
|
||||
self.status = status
|
||||
self.last = None
|
||||
self.stopShowTime = 0.0
|
||||
|
||||
def OnUpdate(self):
|
||||
state = player.AutoHuntGetState()
|
||||
if state != self.last:
|
||||
(stateIndex, reason, noHP, noSP, gathering, gathered) = state
|
||||
wasOn = self.last and self.last[0] != STATE_OFF
|
||||
self.last = state
|
||||
if stateIndex != STATE_OFF:
|
||||
text = L("自动狩猎 · ") + L(STATE_TEXT.get(stateIndex, ""))
|
||||
if gathering:
|
||||
text = L("自动狩猎 · 聚怪中 %d/%d" % (gathered, player.AutoHuntGetConfig()[7]))
|
||||
if noHP:
|
||||
text += L(" · 无红药")
|
||||
if noSP:
|
||||
text += L(" · 无蓝药")
|
||||
self.status.SetStatus(text, COLOR_ON)
|
||||
elif wasOn and reason in STOP_TEXT:
|
||||
self.status.SetStatus(L(STOP_TEXT[reason]), COLOR_OFF)
|
||||
self.stopShowTime = app.GetTime() + 5.0
|
||||
if reason != STOP_USER:
|
||||
chat.AppendChat(chat.CHAT_TYPE_INFO, L(STOP_TEXT[reason]))
|
||||
else:
|
||||
self.status.Hide()
|
||||
elif state[0] == STATE_OFF and self.stopShowTime and app.GetTime() > self.stopShowTime:
|
||||
self.stopShowTime = 0.0
|
||||
self.status.Hide()
|
||||
|
||||
|
||||
g_status = None
|
||||
g_ticker = None
|
||||
g_dialog = None
|
||||
|
||||
|
||||
def Toggle():
|
||||
if player.AutoHuntIsEnabled():
|
||||
player.AutoHuntSetEnabled(0)
|
||||
return
|
||||
player.AutoHuntSetEnabled(1)
|
||||
if player.AutoHuntIsEnabled():
|
||||
chat.AppendChat(chat.CHAT_TYPE_INFO, L("自动狩猎已开启(再次点击AUTO或F9停止)"))
|
||||
|
||||
|
||||
def OpenSettings():
|
||||
if g_dialog:
|
||||
if g_dialog.IsShow():
|
||||
g_dialog.Close()
|
||||
else:
|
||||
g_dialog.Open()
|
||||
|
||||
|
||||
def IsSettingsOpen():
|
||||
return g_dialog and g_dialog.IsShow()
|
||||
|
||||
|
||||
def Attach(gameWindow):
|
||||
global g_status, g_ticker, g_dialog
|
||||
Detach()
|
||||
g_status = StatusBoard()
|
||||
g_ticker = Ticker(g_status)
|
||||
g_ticker.Show()
|
||||
g_dialog = SettingDialog()
|
||||
if gameWindow.onPressKeyDict is not None:
|
||||
gameWindow.onPressKeyDict[app.DIK_F9] = Toggle
|
||||
gameWindow.onPressKeyDict[app.DIK_F8] = OpenSettings
|
||||
|
||||
|
||||
def Detach():
|
||||
global g_status, g_ticker, g_dialog
|
||||
player.AutoHuntSetEnabled(0)
|
||||
if g_dialog:
|
||||
g_dialog.Destroy()
|
||||
if g_ticker:
|
||||
g_ticker.Hide()
|
||||
g_ticker.status = None
|
||||
if g_status:
|
||||
g_status.Hide()
|
||||
g_status = g_ticker = g_dialog = None
|
||||
|
||||
|
||||
def __Install():
|
||||
openGame = game.GameWindow.Open
|
||||
closeGame = game.GameWindow.Close
|
||||
|
||||
def Open(self):
|
||||
openGame(self)
|
||||
Attach(self)
|
||||
|
||||
def Close(self):
|
||||
Detach()
|
||||
closeGame(self)
|
||||
|
||||
game.GameWindow.Open = Open
|
||||
game.GameWindow.Close = Close
|
||||
|
||||
__Install()
|
||||
)PY";
|
||||
Reference in New Issue
Block a user