Files
mtgodot-poc/src/platform/ScriptLib/InventoryPagesScript.inc
T
shenleiandClaude Opus 5.5 e33e41c12b 背包扩展到 5 页:服务端补丁 + DB 迁移 + 客户端 mt_inventory
- 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>
2026-09-29 22:37:44 +09:00

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";