- GameType.h: c_Inventory_Page_Count 2 -> 5; SQuickSlot.Position BYTE -> WORD - quickslot 包/接口 pos 改为 WORD(CG/GC 5 字节),wire_classic 同步 - mt_inventory PORT 模块:inventorywindow.py 装备格 90 -> EQUIPMENT_SLOT_START 重映射、5 个小页签、 自动药水高亮与 SelectItemWindow 扫描全部 5 页;40250 脚本保持不变 - tools/server/inventory-5-pages: server.patch(格子布局 +135、quickslot WORD)、migrate_db.py、README Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
226 lines
6.1 KiB
C++
226 lines
6.1 KiB
C++
// The 5-page bag (PORT; 40250 has 2 pages of 45 cells), run by PythonBoot::RunMainScript as module
|
|
// mt_inventory. GameType.h's c_Inventory_Page_Count = 5 matches the 203 server patch
|
|
// (tools/server/inventory-5-pages), which moves every cell after the bag from 90 to 225. The 40250 scripts
|
|
// stay unchanged; this module patches what they hard-code for 2 pages:
|
|
// - InventoryWindow.py: EQUIPMENT_START_INDEX = 90 is remapped to player.EQUIPMENT_SLOT_START, and the two
|
|
// large page tabs become five small ones (the equipment tabs' image) across the 5x32 grid;
|
|
// - uiInventory.InventoryWindow: five tab events, SetInventoryPage's `inventoryTab[1-page]`, and the
|
|
// auto-potion highlight in RefreshBagSlotWindow, which only subtracts one page;
|
|
// - uiselectitem.SelectItemWindow.RefreshSlot: scans INVENTORY_PAGE_SIZE*2 cells.
|
|
static const char c_szInventoryPagesScript[] = R"PY(# -*- coding: utf-8 -*-
|
|
import constInfo
|
|
import player
|
|
import ui
|
|
import uiInventory
|
|
import uiselectitem
|
|
import item
|
|
|
|
OLD_EQUIPMENT_START = 90
|
|
OLD_EQUIPMENT_END = OLD_EQUIPMENT_START + 32
|
|
PAGE_COUNT = player.INVENTORY_PAGE_COUNT
|
|
PAGE_TEXTS = ("I", "II", "III", "IV", "V")
|
|
SMALL_TAB = "d:/ymir work/ui/game/windows/tab_button_small_%02d.sub"
|
|
TAB_X = 8
|
|
TAB_W = 32
|
|
|
|
|
|
def _Children(node):
|
|
return node.get("children", ())
|
|
|
|
|
|
def _Find(node, name):
|
|
if node.get("name") == name:
|
|
return node
|
|
for child in _Children(node):
|
|
found = _Find(child, name)
|
|
if found:
|
|
return found
|
|
return None
|
|
|
|
|
|
def _PageTab(page, x, y, tooltip):
|
|
tab = {
|
|
"name" : "Inventory_Tab_%02d" % (page + 1),
|
|
"type" : "radio_button",
|
|
"x" : x,
|
|
"y" : y,
|
|
"default_image" : SMALL_TAB % 1,
|
|
"over_image" : SMALL_TAB % 2,
|
|
"down_image" : SMALL_TAB % 3,
|
|
"children" : (
|
|
{
|
|
"name" : "Inventory_Tab_%02d_Print" % (page + 1),
|
|
"type" : "text",
|
|
"x" : 0,
|
|
"y" : 0,
|
|
"all_align" : "center",
|
|
"text" : PAGE_TEXTS[page],
|
|
},
|
|
),
|
|
}
|
|
if tooltip:
|
|
tab["tooltip_text"] = tooltip
|
|
return tab
|
|
|
|
|
|
def _PatchInventoryWindow(window):
|
|
delta = player.EQUIPMENT_SLOT_START - OLD_EQUIPMENT_START
|
|
equip = _Find(window, "EquipmentSlot")
|
|
if equip and delta:
|
|
slots = []
|
|
for slot in equip["slot"]:
|
|
if OLD_EQUIPMENT_START <= slot["index"] < OLD_EQUIPMENT_END:
|
|
slot = dict(slot)
|
|
slot["index"] += delta
|
|
slots.append(slot)
|
|
equip["slot"] = tuple(slots)
|
|
|
|
board = _Find(window, "board")
|
|
first = _Find(window, "Inventory_Tab_01")
|
|
if not board or not first:
|
|
return
|
|
tooltips = [first.get("tooltip_text")]
|
|
second = _Find(window, "Inventory_Tab_02")
|
|
tooltips.append(second.get("tooltip_text") if second else None)
|
|
children = []
|
|
for child in board["children"]:
|
|
name = child.get("name", "")
|
|
if name.startswith("Inventory_Tab_"):
|
|
if name == "Inventory_Tab_01":
|
|
for page in xrange(PAGE_COUNT):
|
|
tip = tooltips[page] if page < len(tooltips) else None
|
|
children.append(_PageTab(page, TAB_X + TAB_W * page, first["y"], tip))
|
|
continue
|
|
children.append(child)
|
|
board["children"] = tuple(children)
|
|
|
|
|
|
def _InstallScriptPatch():
|
|
execfile = ui.execfile
|
|
|
|
def ExecFile(fileName, dict):
|
|
execfile(fileName, dict)
|
|
if fileName.replace("\\", "/").split("/")[-1].lower() == "inventorywindow.py" and "window" in dict:
|
|
_PatchInventoryWindow(dict["window"])
|
|
|
|
ui.execfile = ExecFile
|
|
|
|
|
|
def _InstallInventoryWindow():
|
|
cls = uiInventory.InventoryWindow
|
|
load = cls._InventoryWindow__LoadWindow
|
|
refresh = cls.RefreshBagSlotWindow
|
|
|
|
def LoadWindow(self):
|
|
loaded = self.isLoaded
|
|
load(self)
|
|
if loaded:
|
|
return
|
|
try:
|
|
self.inventoryTab = [self.GetChild("Inventory_Tab_%02d" % (page + 1)) for page in xrange(PAGE_COUNT)]
|
|
for page in xrange(PAGE_COUNT):
|
|
self.inventoryTab[page].SetEvent(lambda arg=page: self.SetInventoryPage(arg))
|
|
self.inventoryTab[0].Down()
|
|
except:
|
|
import dbg
|
|
dbg.TraceError("mt_inventory: cannot bind the page tabs")
|
|
|
|
def SetInventoryPage(self, page):
|
|
self.inventoryPageIndex = page
|
|
for i in xrange(len(self.inventoryTab)):
|
|
if i != page:
|
|
self.inventoryTab[i].SetUp()
|
|
self.RefreshBagSlotWindow()
|
|
|
|
def RefreshBagSlotWindow(self):
|
|
refresh(self)
|
|
page = getattr(self, "inventoryPageIndex", 0)
|
|
if page < 2:
|
|
return
|
|
# 40250 turns a cell into a slot by subtracting one page, so pages 3-5 highlight nothing.
|
|
for i in xrange(player.INVENTORY_PAGE_SIZE):
|
|
slotNumber = page * player.INVENTORY_PAGE_SIZE + i
|
|
if not player.GetItemCount(slotNumber):
|
|
continue
|
|
if not constInfo.IS_AUTO_POTION(player.GetItemIndex(slotNumber)):
|
|
continue
|
|
if player.GetItemMetinSocket(slotNumber, 0):
|
|
self.wndItem.ActivateSlot(i)
|
|
else:
|
|
self.wndItem.DeactivateSlot(i)
|
|
self.wndItem.RefreshSlot()
|
|
|
|
cls._InventoryWindow__LoadWindow = LoadWindow
|
|
cls.SetInventoryPage = SetInventoryPage
|
|
cls.RefreshBagSlotWindow = RefreshBagSlotWindow
|
|
|
|
|
|
def _InstallSelectItem():
|
|
# SelectItemWindow.RefreshSlot with the cell loop over every page instead of INVENTORY_PAGE_SIZE*2.
|
|
def RefreshSlot(self):
|
|
slotPos = 0
|
|
self.inventorySlotPosDict = {}
|
|
|
|
getItemVNum=player.GetItemIndex
|
|
getItemCount=player.GetItemCount
|
|
setItemVNum=self.itemSlot.SetItemSlot
|
|
|
|
for i in xrange(player.INVENTORY_PAGE_SIZE*PAGE_COUNT):
|
|
slotNumber = i
|
|
|
|
itemVNum = getItemVNum(slotNumber)
|
|
if 0 == itemVNum:
|
|
continue
|
|
|
|
if not item.IsMetin(itemVNum):
|
|
continue
|
|
|
|
itemGrade = player.GetItemGrade(slotNumber)
|
|
if itemGrade > 2:
|
|
continue
|
|
|
|
self.inventorySlotPosDict[slotPos] = i
|
|
|
|
slotPos += 1
|
|
|
|
if slotPos > 54:
|
|
break
|
|
|
|
itemCount = len(self.inventorySlotPosDict)
|
|
if itemCount < 15:
|
|
self.SetTableSize(3)
|
|
|
|
else:
|
|
lineCount = 3
|
|
lineCount += (itemCount - 15) / 5
|
|
if itemCount % 5:
|
|
lineCount += 1
|
|
self.SetTableSize(lineCount)
|
|
|
|
for selectWndPos, inventoryPos in self.inventorySlotPosDict.items():
|
|
itemVNum = getItemVNum(inventoryPos)
|
|
itemCount = getItemCount(inventoryPos)
|
|
|
|
if itemCount <= 1:
|
|
itemCount = 0
|
|
|
|
setItemVNum(selectWndPos, itemVNum, itemCount)
|
|
|
|
self.itemSlot.RefreshSlot()
|
|
|
|
uiselectitem.SelectItemWindow.RefreshSlot = RefreshSlot
|
|
|
|
|
|
def __Install():
|
|
if PAGE_COUNT <= 2:
|
|
return
|
|
for install in (_InstallScriptPatch, _InstallInventoryWindow, _InstallSelectItem):
|
|
try:
|
|
install()
|
|
except:
|
|
import dbg
|
|
dbg.TraceError("mt_inventory: %s failed" % install.__name__)
|
|
|
|
__Install()
|
|
)PY";
|