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)