forked from metin-server/m2dev-client
456 lines
14 KiB
Python
456 lines
14 KiB
Python
import item
|
|
import net
|
|
import player
|
|
import ui
|
|
import uitooltip
|
|
|
|
|
|
STATE_SPEED = 1
|
|
STATE_ACTIVE_COUNT = 0
|
|
STATE_SCROLL_COUNT = 0
|
|
STATE_ETA_SECONDS = 0
|
|
STATE_SLOTS = []
|
|
OPEN_WINDOWS = []
|
|
_AFFECT_HELPER = None
|
|
|
|
for _slotIndex in range(5):
|
|
STATE_SLOTS.append({
|
|
"active": 0,
|
|
"itemCell": 0,
|
|
"attrType": 0,
|
|
"minValue": 0,
|
|
"attempts": 0,
|
|
})
|
|
|
|
|
|
def _GetAffectHelper():
|
|
global _AFFECT_HELPER
|
|
|
|
if _AFFECT_HELPER is None:
|
|
_AFFECT_HELPER = uitooltip.ItemToolTip()
|
|
|
|
return _AFFECT_HELPER
|
|
|
|
|
|
def _GetAffectLabel(attrType):
|
|
if attrType <= 0:
|
|
return "-"
|
|
|
|
try:
|
|
label = _GetAffectHelper()._ItemToolTip__GetAffectString(attrType, 1)
|
|
except:
|
|
label = None
|
|
|
|
if not label:
|
|
return "Attr %d" % attrType
|
|
|
|
return label
|
|
|
|
|
|
ATTR_OPTIONS = (
|
|
(item.APPLY_MAX_HP, _GetAffectLabel(item.APPLY_MAX_HP)),
|
|
(item.APPLY_MAX_SP, _GetAffectLabel(item.APPLY_MAX_SP)),
|
|
(item.APPLY_STR, _GetAffectLabel(item.APPLY_STR)),
|
|
(item.APPLY_DEX, _GetAffectLabel(item.APPLY_DEX)),
|
|
(item.APPLY_CON, _GetAffectLabel(item.APPLY_CON)),
|
|
(item.APPLY_INT, _GetAffectLabel(item.APPLY_INT)),
|
|
(item.APPLY_ATT_SPEED, _GetAffectLabel(item.APPLY_ATT_SPEED)),
|
|
(item.APPLY_MOV_SPEED, _GetAffectLabel(item.APPLY_MOV_SPEED)),
|
|
(item.APPLY_CAST_SPEED, _GetAffectLabel(item.APPLY_CAST_SPEED)),
|
|
(item.APPLY_CRITICAL_PCT, _GetAffectLabel(item.APPLY_CRITICAL_PCT)),
|
|
(item.APPLY_PENETRATE_PCT, _GetAffectLabel(item.APPLY_PENETRATE_PCT)),
|
|
(item.APPLY_ATTBONUS_MONSTER, _GetAffectLabel(item.APPLY_ATTBONUS_MONSTER)),
|
|
(item.APPLY_ATTBONUS_DEVIL, _GetAffectLabel(item.APPLY_ATTBONUS_DEVIL)),
|
|
(item.APPLY_ATTBONUS_HUMAN, _GetAffectLabel(item.APPLY_ATTBONUS_HUMAN)),
|
|
(item.APPLY_STEAL_HP, _GetAffectLabel(item.APPLY_STEAL_HP)),
|
|
(item.APPLY_STEAL_SP, _GetAffectLabel(item.APPLY_STEAL_SP)),
|
|
(item.APPLY_BLOCK, _GetAffectLabel(item.APPLY_BLOCK)),
|
|
(item.APPLY_DODGE, _GetAffectLabel(item.APPLY_DODGE)),
|
|
(item.APPLY_ATT_GRADE_BONUS, _GetAffectLabel(item.APPLY_ATT_GRADE_BONUS)),
|
|
(item.APPLY_DEF_GRADE_BONUS, _GetAffectLabel(item.APPLY_DEF_GRADE_BONUS)),
|
|
(item.APPLY_RESIST_SWORD, _GetAffectLabel(item.APPLY_RESIST_SWORD)),
|
|
(item.APPLY_RESIST_TWOHAND, _GetAffectLabel(item.APPLY_RESIST_TWOHAND)),
|
|
(item.APPLY_RESIST_DAGGER, _GetAffectLabel(item.APPLY_RESIST_DAGGER)),
|
|
(item.APPLY_RESIST_BOW, _GetAffectLabel(item.APPLY_RESIST_BOW)),
|
|
(item.APPLY_RESIST_FIRE, _GetAffectLabel(item.APPLY_RESIST_FIRE)),
|
|
(item.APPLY_RESIST_ELEC, _GetAffectLabel(item.APPLY_RESIST_ELEC)),
|
|
(item.APPLY_RESIST_MAGIC, _GetAffectLabel(item.APPLY_RESIST_MAGIC)),
|
|
(item.APPLY_RESIST_WIND, _GetAffectLabel(item.APPLY_RESIST_WIND)),
|
|
(item.APPLY_RESIST_ICE, _GetAffectLabel(item.APPLY_RESIST_ICE)),
|
|
(item.APPLY_RESIST_EARTH, _GetAffectLabel(item.APPLY_RESIST_EARTH)),
|
|
(item.APPLY_RESIST_DARK, _GetAffectLabel(item.APPLY_RESIST_DARK)),
|
|
)
|
|
|
|
SPEED_OPTIONS = (
|
|
(0, "Slow / 3s"),
|
|
(1, "Normal / 2s"),
|
|
(2, "Fast / 1s"),
|
|
)
|
|
|
|
|
|
def SetSwitchbotState(speedIndex, activeCount, scrollCount, etaSeconds):
|
|
global STATE_SPEED
|
|
global STATE_ACTIVE_COUNT
|
|
global STATE_SCROLL_COUNT
|
|
global STATE_ETA_SECONDS
|
|
|
|
STATE_SPEED = int(speedIndex)
|
|
STATE_ACTIVE_COUNT = max(0, int(activeCount))
|
|
STATE_SCROLL_COUNT = max(0, int(scrollCount))
|
|
STATE_ETA_SECONDS = max(0, int(etaSeconds))
|
|
|
|
for window in OPEN_WINDOWS:
|
|
try:
|
|
window.Refresh()
|
|
except:
|
|
pass
|
|
|
|
|
|
def SetSwitchbotSlotState(slotIndex, active, itemCell, attrType, minValue, attempts):
|
|
slotIndex = int(slotIndex)
|
|
if slotIndex < 0 or slotIndex >= len(STATE_SLOTS):
|
|
return
|
|
|
|
STATE_SLOTS[slotIndex]["active"] = 1 if int(active) else 0
|
|
STATE_SLOTS[slotIndex]["itemCell"] = int(itemCell)
|
|
STATE_SLOTS[slotIndex]["attrType"] = int(attrType)
|
|
STATE_SLOTS[slotIndex]["minValue"] = int(minValue)
|
|
STATE_SLOTS[slotIndex]["attempts"] = int(attempts)
|
|
|
|
for window in OPEN_WINDOWS:
|
|
try:
|
|
window.Refresh()
|
|
except:
|
|
pass
|
|
|
|
|
|
class SwitchbotWindow(ui.BoardWithTitleBar):
|
|
CANDIDATE_PAGE_SIZE = 12
|
|
|
|
def __init__(self):
|
|
ui.BoardWithTitleBar.__init__(self)
|
|
|
|
OPEN_WINDOWS.append(self)
|
|
|
|
self.selectedSlotIndex = 0
|
|
self.pageIndex = 0
|
|
self.candidateSlots = []
|
|
self.slotWidgets = []
|
|
self.candidateButtons = []
|
|
self.speedLabels = {}
|
|
self.attrLabels = {}
|
|
|
|
self.AddFlag("float")
|
|
self.AddFlag("movable")
|
|
self.SetSize(535, 406)
|
|
self.SetTitleName("Switchbot")
|
|
self.SetCloseEvent(self.Hide)
|
|
|
|
self.__CreateChildren()
|
|
self.Hide()
|
|
|
|
def __del__(self):
|
|
if self in OPEN_WINDOWS:
|
|
OPEN_WINDOWS.remove(self)
|
|
ui.BoardWithTitleBar.__del__(self)
|
|
|
|
def Destroy(self):
|
|
if self in OPEN_WINDOWS:
|
|
OPEN_WINDOWS.remove(self)
|
|
self.ClearDictionary()
|
|
self.slotWidgets = []
|
|
self.candidateButtons = []
|
|
self.candidateSlots = []
|
|
self.speedLabels = {}
|
|
self.attrLabels = {}
|
|
|
|
def __CreateChildren(self):
|
|
self.statusLine = self.__CreateLine(15, 36)
|
|
self.scrollLine = self.__CreateLine(15, 56)
|
|
self.etaLine = self.__CreateLine(15, 76)
|
|
self.helpLine = self.__CreateLine(15, 96)
|
|
|
|
self.speedCombo = ui.ComboBox()
|
|
self.speedCombo.SetParent(self)
|
|
self.speedCombo.SetPosition(398, 34)
|
|
self.speedCombo.SetSize(120, 18)
|
|
self.speedCombo.SetEvent(self.__OnChangeSpeed)
|
|
self.speedCombo.Show()
|
|
|
|
for (speedIndex, label) in SPEED_OPTIONS:
|
|
self.speedCombo.InsertItem(speedIndex, label)
|
|
self.speedLabels[speedIndex] = label
|
|
|
|
for slotIndex in range(5):
|
|
baseY = 126 + slotIndex * 38
|
|
row = {}
|
|
|
|
row["selectButton"] = self.__CreateButton(15, baseY, 48, "Slot %d" % (slotIndex + 1))
|
|
row["selectButton"].SetEvent(self.__SelectSlot, slotIndex)
|
|
|
|
row["itemLine"] = self.__CreateLine(72, baseY + 2)
|
|
row["statusLine"] = self.__CreateLine(72, baseY + 18)
|
|
|
|
row["attrCombo"] = ui.ComboBox()
|
|
row["attrCombo"].SetParent(self)
|
|
row["attrCombo"].SetPosition(220, baseY)
|
|
row["attrCombo"].SetSize(165, 18)
|
|
row["attrCombo"].SetEvent(lambda attrType, line=slotIndex: self.__SetAttrType(line, attrType))
|
|
row["attrCombo"].Show()
|
|
|
|
for (attrType, label) in ATTR_OPTIONS:
|
|
row["attrCombo"].InsertItem(attrType, label)
|
|
|
|
attrBar = ui.SlotBar()
|
|
attrBar.SetParent(self)
|
|
attrBar.SetPosition(392, baseY)
|
|
attrBar.SetSize(48, 18)
|
|
attrBar.Show()
|
|
|
|
row["valueBar"] = attrBar
|
|
row["valueEdit"] = ui.EditLine()
|
|
row["valueEdit"].SetParent(attrBar)
|
|
row["valueEdit"].SetPosition(3, 3)
|
|
row["valueEdit"].SetSize(42, 12)
|
|
row["valueEdit"].SetMax(5)
|
|
row["valueEdit"].SetNumberMode()
|
|
row["valueEdit"].Show()
|
|
|
|
row["startButton"] = self.__CreateButton(447, baseY, 40, "Start")
|
|
row["startButton"].SetEvent(self.__ToggleSlot, slotIndex)
|
|
row["clearButton"] = self.__CreateButton(492, baseY, 28, "X")
|
|
row["clearButton"].SetEvent(self.__ClearSlot, slotIndex)
|
|
|
|
self.slotWidgets.append(row)
|
|
|
|
self.prevButton = self.__CreateButton(15, 330, 44, "Prev")
|
|
self.prevButton.SetEvent(self.__ChangePage, -1)
|
|
self.pageLine = self.__CreateLine(80, 334)
|
|
self.nextButton = self.__CreateButton(170, 330, 44, "Next")
|
|
self.nextButton.SetEvent(self.__ChangePage, 1)
|
|
|
|
for index in range(self.CANDIDATE_PAGE_SIZE):
|
|
column = index % 2
|
|
row = index // 2
|
|
button = self.__CreateButton(15 + column * 155, 356 + row * 18, 148, "-")
|
|
button.SetEvent(self.__AssignCandidate, index)
|
|
self.candidateButtons.append(button)
|
|
|
|
self.syncButton = self.__CreateButton(305, 330, 54, "Sync")
|
|
self.syncButton.SetEvent(self.__Sync)
|
|
self.startAllButton = self.__CreateButton(366, 330, 74, "Start All")
|
|
self.startAllButton.SetEvent(self.__StartAll)
|
|
self.stopAllButton = self.__CreateButton(446, 330, 74, "Stop All")
|
|
self.stopAllButton.SetEvent(self.__StopAll)
|
|
|
|
def __CreateLine(self, x, y):
|
|
textLine = ui.TextLine()
|
|
textLine.SetParent(self)
|
|
textLine.SetPosition(x, y)
|
|
textLine.SetOutline()
|
|
textLine.Show()
|
|
return textLine
|
|
|
|
def __CreateButton(self, x, y, width, text):
|
|
button = ui.Button()
|
|
button.SetParent(self)
|
|
button.SetPosition(x, y)
|
|
button.SetUpVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
|
|
button.SetOverVisual("d:/ymir work/ui/public/small_thin_button_02.sub")
|
|
button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
|
|
button.SetDisableVisual("d:/ymir work/ui/public/small_thin_button_01.sub")
|
|
button.SetSize(width, 17)
|
|
button.SetText(text)
|
|
button.Show()
|
|
return button
|
|
|
|
def __Send(self, command):
|
|
net.SendChatPacket(command, 0)
|
|
|
|
def __IsEligibleItem(self, slotPos):
|
|
itemVnum = player.GetItemIndex(slotPos)
|
|
if itemVnum == 0:
|
|
return False
|
|
|
|
item.SelectItem(itemVnum)
|
|
if item.GetItemType() == item.ITEM_TYPE_COSTUME:
|
|
return False
|
|
|
|
if item.GetItemType() not in (item.ITEM_TYPE_WEAPON, item.ITEM_TYPE_ARMOR):
|
|
return False
|
|
|
|
for attrIndex in range(player.ATTRIBUTE_SLOT_MAX_NUM):
|
|
if player.GetItemAttribute(slotPos, attrIndex)[0] != 0:
|
|
return True
|
|
|
|
return False
|
|
|
|
def __GetItemLabel(self, slotPos):
|
|
itemVnum = player.GetItemIndex(slotPos)
|
|
if itemVnum == 0:
|
|
return "Slot %d" % slotPos
|
|
|
|
item.SelectItem(itemVnum)
|
|
return "%d: %s" % (slotPos, item.GetItemName())
|
|
|
|
def __BuildCandidateSlots(self):
|
|
self.candidateSlots = []
|
|
|
|
for slotPos in range(player.INVENTORY_PAGE_SIZE * player.INVENTORY_PAGE_COUNT):
|
|
if self.__IsEligibleItem(slotPos):
|
|
self.candidateSlots.append(slotPos)
|
|
|
|
def __FormatEta(self):
|
|
if STATE_ETA_SECONDS <= 0:
|
|
return "ETA: -"
|
|
|
|
minutes = STATE_ETA_SECONDS // 60
|
|
seconds = STATE_ETA_SECONDS % 60
|
|
return "ETA: %02d:%02d" % (minutes, seconds)
|
|
|
|
def __GetAttrType(self, slotIndex):
|
|
return STATE_SLOTS[slotIndex]["attrType"]
|
|
|
|
def __GetMinValue(self, slotIndex):
|
|
text = self.slotWidgets[slotIndex]["valueEdit"].GetText()
|
|
if not text:
|
|
return STATE_SLOTS[slotIndex]["minValue"]
|
|
return int(text)
|
|
|
|
def __SelectSlot(self, slotIndex):
|
|
self.selectedSlotIndex = slotIndex
|
|
self.Refresh()
|
|
|
|
def __SetAttrType(self, slotIndex, attrType):
|
|
SetSwitchbotSlotState(
|
|
slotIndex,
|
|
STATE_SLOTS[slotIndex]["active"],
|
|
STATE_SLOTS[slotIndex]["itemCell"],
|
|
attrType,
|
|
STATE_SLOTS[slotIndex]["minValue"],
|
|
STATE_SLOTS[slotIndex]["attempts"],
|
|
)
|
|
|
|
def __AssignCandidate(self, localIndex):
|
|
candidateIndex = self.pageIndex * self.CANDIDATE_PAGE_SIZE + localIndex
|
|
if candidateIndex < 0 or candidateIndex >= len(self.candidateSlots):
|
|
return
|
|
|
|
slotIndex = self.selectedSlotIndex
|
|
SetSwitchbotSlotState(
|
|
slotIndex,
|
|
0,
|
|
self.candidateSlots[candidateIndex],
|
|
STATE_SLOTS[slotIndex]["attrType"],
|
|
STATE_SLOTS[slotIndex]["minValue"],
|
|
0,
|
|
)
|
|
|
|
def __ToggleSlot(self, slotIndex):
|
|
slotState = STATE_SLOTS[slotIndex]
|
|
if slotState["active"]:
|
|
self.__Send("/switchbot stop %d" % slotIndex)
|
|
return
|
|
|
|
itemCell = slotState["itemCell"]
|
|
attrType = self.__GetAttrType(slotIndex)
|
|
minValue = self.__GetMinValue(slotIndex)
|
|
if not self.__IsEligibleItem(itemCell) or attrType <= 0 or minValue <= 0:
|
|
return
|
|
|
|
SetSwitchbotSlotState(slotIndex, 0, itemCell, attrType, minValue, slotState["attempts"])
|
|
self.__Send("/switchbot start %d %d %d %d" % (slotIndex, itemCell, attrType, minValue))
|
|
|
|
def __ClearSlot(self, slotIndex):
|
|
self.__Send("/switchbot clear %d" % slotIndex)
|
|
|
|
def __ChangePage(self, delta):
|
|
totalPages = max(1, (len(self.candidateSlots) + self.CANDIDATE_PAGE_SIZE - 1) // self.CANDIDATE_PAGE_SIZE)
|
|
self.pageIndex = max(0, min(totalPages - 1, self.pageIndex + delta))
|
|
self.Refresh()
|
|
|
|
def __OnChangeSpeed(self, speedIndex):
|
|
self.__Send("/switchbot speed %d" % int(speedIndex))
|
|
|
|
def __Sync(self):
|
|
self.__Send("/switchbot sync")
|
|
|
|
def __StartAll(self):
|
|
for slotIndex in range(len(STATE_SLOTS)):
|
|
slotState = STATE_SLOTS[slotIndex]
|
|
if slotState["active"]:
|
|
continue
|
|
|
|
itemCell = slotState["itemCell"]
|
|
attrType = self.__GetAttrType(slotIndex)
|
|
minValue = self.__GetMinValue(slotIndex)
|
|
if not self.__IsEligibleItem(itemCell) or attrType <= 0 or minValue <= 0:
|
|
continue
|
|
|
|
self.__Send("/switchbot start %d %d %d %d" % (slotIndex, itemCell, attrType, minValue))
|
|
|
|
def __StopAll(self):
|
|
self.__Send("/switchbot stop_all")
|
|
|
|
def Refresh(self):
|
|
self.__BuildCandidateSlots()
|
|
|
|
self.statusLine.SetText("Status: %d active slot(s)" % STATE_ACTIVE_COUNT)
|
|
self.scrollLine.SetText("Switch items: %d" % STATE_SCROLL_COUNT)
|
|
self.etaLine.SetText(self.__FormatEta())
|
|
self.helpLine.SetText("Select a row, assign an item, pick a target bonus and min value")
|
|
self.speedCombo.SetCurrentItem(self.speedLabels.get(STATE_SPEED, SPEED_OPTIONS[1][1]))
|
|
|
|
for slotIndex in range(len(self.slotWidgets)):
|
|
slotState = STATE_SLOTS[slotIndex]
|
|
row = self.slotWidgets[slotIndex]
|
|
row["selectButton"].SetText(("> " if self.selectedSlotIndex == slotIndex else "") + "S%d" % (slotIndex + 1))
|
|
row["itemLine"].SetText("Item: %s" % ("-" if not self.__IsEligibleItem(slotState["itemCell"]) else self.__GetItemLabel(slotState["itemCell"])))
|
|
row["statusLine"].SetText("Target: %s / tries %d / %s" % (
|
|
_GetAffectLabel(slotState["attrType"]),
|
|
slotState["attempts"],
|
|
"Running" if slotState["active"] else "Ready",
|
|
))
|
|
|
|
if slotState["attrType"] > 0:
|
|
row["attrCombo"].SetCurrentItem(_GetAffectLabel(slotState["attrType"]))
|
|
else:
|
|
row["attrCombo"].SetCurrentItem("-")
|
|
|
|
if slotState["minValue"] > 0 and row["valueEdit"].GetText() != str(slotState["minValue"]):
|
|
row["valueEdit"].SetText(str(slotState["minValue"]))
|
|
elif slotState["minValue"] <= 0 and row["valueEdit"].GetText():
|
|
row["valueEdit"].SetText("")
|
|
|
|
row["startButton"].SetText("Stop" if slotState["active"] else "Start")
|
|
|
|
start = self.pageIndex * self.CANDIDATE_PAGE_SIZE
|
|
end = start + self.CANDIDATE_PAGE_SIZE
|
|
pageSlots = self.candidateSlots[start:end]
|
|
totalPages = max(1, (len(self.candidateSlots) + self.CANDIDATE_PAGE_SIZE - 1) // self.CANDIDATE_PAGE_SIZE)
|
|
self.pageLine.SetText("Candidates %d / %d" % (self.pageIndex + 1, totalPages))
|
|
|
|
for localIndex in range(len(self.candidateButtons)):
|
|
button = self.candidateButtons[localIndex]
|
|
if localIndex < len(pageSlots):
|
|
button.SetText(self.__GetItemLabel(pageSlots[localIndex]))
|
|
button.Enable()
|
|
else:
|
|
button.SetText("-")
|
|
button.Disable()
|
|
|
|
if self.pageIndex > 0:
|
|
self.prevButton.Enable()
|
|
else:
|
|
self.prevButton.Disable()
|
|
|
|
if end < len(self.candidateSlots):
|
|
self.nextButton.Enable()
|
|
else:
|
|
self.nextButton.Disable()
|
|
|
|
def Show(self):
|
|
self.__Sync()
|
|
self.Refresh()
|
|
ui.BoardWithTitleBar.Show(self)
|