# belt_system.gd —— Metin2 40250 腰带系统与专属药水背包 1:1 # 对照 40250 服务端 belt.cpp, char_item.cpp, uiinventory.py class_name BeltSystem extends RefCounted signal belt_equipped(belt_name: String, slots_unlocked: int) signal belt_unequipped() signal belt_item_stored(slot: int, item_name: String) signal belt_item_removed(slot: int) const MAX_BELT_SLOTS := 16 # 最大 4x4 空间 # 允许存入腰带的消耗品/药水类型 const ALLOWED_CONSUMABLE_TYPES: Array = ["potion", "consumable", "dew", "elixir"] # 已装备的腰带 var equipped_belt: Dictionary = {} var belt_inventory: Array = [] # 16 个格子 func _init() -> void: belt_inventory.resize(MAX_BELT_SLOTS) for i in range(MAX_BELT_SLOTS): belt_inventory[i] = null # 根据腰带强化等级计算开放的药水槽位数量 static func calculate_unlocked_slots(refine_level: int) -> int: if refine_level >= 9: return 16 # 4x4 elif refine_level >= 7: return 12 # 3x4 elif refine_level >= 5: return 10 elif refine_level >= 3: return 8 # 2x4 elif refine_level >= 1: return 6 else: return 4 # 1x4 # 获取当前开放的药水格子数 func get_unlocked_slot_count() -> int: if equipped_belt.is_empty(): return 0 var refine: int = int(equipped_belt.get("refine_level", 0)) return calculate_unlocked_slots(refine) # 佩戴腰带 func equip_belt(belt_item: Dictionary) -> Dictionary: if belt_item.is_empty(): return {"ok": false, "reason": "EMPTY_BELT", "msg": "腰带数据为空!"} equipped_belt = belt_item.duplicate(true) var unlocked := get_unlocked_slot_count() var bname: String = equipped_belt.get("name", "腰带") belt_equipped.emit(bname, unlocked) return { "ok": true, "belt": equipped_belt, "unlocked_slots": unlocked, "msg": "已佩戴【%s】,解锁了 %d 格专属药水空间!" % [bname, unlocked] } # 卸下腰带 (需确保腰带内物品能够全部安全移出或为空) func unequip_belt(main_inventory: Array) -> Dictionary: if equipped_belt.is_empty(): return {"ok": false, "reason": "NO_BELT_EQUIPPED", "msg": "当前未佩戴腰带!"} # 检查腰带内是否有物品 var items_in_belt: Array = [] for i in range(MAX_BELT_SLOTS): if belt_inventory[i] != null: items_in_belt.append({"belt_slot": i, "item": belt_inventory[i]}) if not items_in_belt.is_empty(): # 统计主背包空位 var empty_slots: Array = [] for j in range(main_inventory.size()): if main_inventory[j] == null: empty_slots.append(j) if empty_slots.size() < items_in_belt.size(): return { "ok": false, "reason": "BELT_NOT_EMPTY", "items_count": items_in_belt.size(), "empty_slots": empty_slots.size(), "msg": "主背包空间不足以容纳腰带内的药水,无法卸下腰带!" } # 移入主背包 for k in range(items_in_belt.size()): var m_slot: int = empty_slots[k] var it = items_in_belt[k]["item"] var b_slot: int = items_in_belt[k]["belt_slot"] main_inventory[m_slot] = it belt_inventory[b_slot] = null var old_belt := equipped_belt.duplicate(true) equipped_belt.clear() belt_unequipped.emit() return { "ok": true, "belt": old_belt, "msg": "腰带已卸下!内部药水已全部归还至主背包。" } # 存放药水至腰带专属背包 func store_item_in_belt(slot_idx: int, item: Dictionary) -> Dictionary: if equipped_belt.is_empty(): return {"ok": false, "reason": "NO_BELT_EQUIPPED", "msg": "未佩戴腰带,无法使用专属药水栏!"} var unlocked := get_unlocked_slot_count() if slot_idx < 0 or slot_idx >= unlocked: return { "ok": false, "reason": "SLOT_LOCKED_OR_INVALID", "unlocked_slots": unlocked, "msg": "该腰带槽位尚未解锁或无效!当前腰带只开放了前 %d 格。" % unlocked } # 药品类型校验 var it_type: String = item.get("item_type", "") var is_potion: bool = ALLOWED_CONSUMABLE_TYPES.has(it_type) or item.get("is_potion", false) if not is_potion: return { "ok": false, "reason": "NOT_A_POTION", "msg": "腰带栏位专用于补给品,只能存放药水、灵药与神圣露水!" } belt_inventory[slot_idx] = item.duplicate(true) var iname: String = item.get("name", "药水") belt_item_stored.emit(slot_idx, iname) return { "ok": true, "slot_idx": slot_idx, "item": belt_inventory[slot_idx], "msg": "已将【%s】置入腰带快捷栏!" % iname } # 从腰带栏取出药水 func take_item_from_belt(slot_idx: int) -> Dictionary: if slot_idx < 0 or slot_idx >= MAX_BELT_SLOTS: return {"ok": false, "reason": "INVALID_SLOT"} var item = belt_inventory[slot_idx] if item == null: return {"ok": false, "reason": "EMPTY_SLOT"} belt_inventory[slot_idx] = null belt_item_removed.emit(slot_idx) return { "ok": true, "slot_idx": slot_idx, "item": item }