# costume_system.gd —— Metin2 40250 独立时装发型与武器衣柜系统 1:1 # 对照 40250 服务端 costume.cpp, char_item.cpp, uiinventory.py, item_data.h class_name CostumeSystem extends RefCounted signal costume_equipped(slot_type: int, item: Dictionary) signal costume_unequipped(slot_type: int) signal costume_expired(slot_type: int, item_name: String) enum CostumeSlot { COSTUME_BODY = 0, # 时装衣服 COSTUME_HAIR = 1, # 时装发型 COSTUME_WEAPON = 2 # 时装武器外观 } const DEFAULT_COSTUME_DURATION := 604800.0 # 默认 7 天 (7 * 86400) var equipped_costumes: Dictionary = { CostumeSlot.COSTUME_BODY: null, CostumeSlot.COSTUME_HAIR: null, CostumeSlot.COSTUME_WEAPON: null } # 穿戴时装 func equip_costume(slot_type: int, costume_item: Dictionary) -> Dictionary: if not equipped_costumes.has(slot_type): return {"ok": false, "reason": "INVALID_SLOT", "msg": "无效的时装栏位!"} if costume_item.is_empty(): return {"ok": false, "reason": "EMPTY_ITEM", "msg": "时装物品数据为空!"} # 校验时装部位匹配 var c_type: String = costume_item.get("costume_type", "") match slot_type: CostumeSlot.COSTUME_BODY: if c_type != "body": return {"ok": false, "reason": "SLOT_TYPE_MISMATCH", "msg": "此物品不是时装衣服!"} CostumeSlot.COSTUME_HAIR: if c_type != "hair": return {"ok": false, "reason": "SLOT_TYPE_MISMATCH", "msg": "此物品不是时装发型!"} CostumeSlot.COSTUME_WEAPON: if c_type != "weapon": return {"ok": false, "reason": "SLOT_TYPE_MISMATCH", "msg": "此物品不是时装武器外观!"} var old_item = equipped_costumes[slot_type] var item_to_equip = costume_item.duplicate(true) if not item_to_equip.has("remaining_time"): item_to_equip["remaining_time"] = DEFAULT_COSTUME_DURATION equipped_costumes[slot_type] = item_to_equip costume_equipped.emit(slot_type, item_to_equip) return { "ok": true, "slot_type": slot_type, "item": item_to_equip, "old_item": old_item, "msg": "时装【%s】已成功穿戴!" % item_to_equip.get("name", "时装") } # 卸下时装 func unequip_costume(slot_type: int) -> Dictionary: if not equipped_costumes.has(slot_type): return {"ok": false, "reason": "INVALID_SLOT", "msg": "无效的时装栏位!"} var item = equipped_costumes[slot_type] if item == null: return {"ok": false, "reason": "SLOT_EMPTY", "msg": "该时装栏位未穿戴时装!"} equipped_costumes[slot_type] = null costume_unequipped.emit(slot_type) return { "ok": true, "slot_type": slot_type, "item": item, "msg": "已卸下时装【%s】。" % item.get("name", "时装") } # 汇总所有已穿戴时装的战力属性 func get_costume_bonuses() -> Dictionary: var total_bonuses: Dictionary = {} for slot in equipped_costumes.keys(): var it = equipped_costumes[slot] if it == null: continue var bonuses: Dictionary = it.get("bonuses", {}) for b_key in bonuses.keys(): var val: int = int(bonuses[b_key]) total_bonuses[b_key] = total_bonuses.get(b_key, 0) + val return total_bonuses # 获取 3D 外观覆盖模型 ID func get_appearance_overrides() -> Dictionary: var overrides: Dictionary = { "body_model": 0, "hair_model": 0, "weapon_model": 0 } if equipped_costumes[CostumeSlot.COSTUME_BODY] != null: overrides["body_model"] = int(equipped_costumes[CostumeSlot.COSTUME_BODY].get("model_id", 0)) if equipped_costumes[CostumeSlot.COSTUME_HAIR] != null: overrides["hair_model"] = int(equipped_costumes[CostumeSlot.COSTUME_HAIR].get("model_id", 0)) if equipped_costumes[CostumeSlot.COSTUME_WEAPON] != null: overrides["weapon_model"] = int(equipped_costumes[CostumeSlot.COSTUME_WEAPON].get("model_id", 0)) return overrides # 逐帧时效衰减更新 func update(dt: float) -> Array: var expired_list: Array = [] for slot in equipped_costumes.keys(): var it = equipped_costumes[slot] if it == null: continue it["remaining_time"] = maxf(0.0, float(it.get("remaining_time", 0.0)) - dt) if it["remaining_time"] <= 0.0: var iname: String = it.get("name", "时装") expired_list.append({"slot": slot, "item": it}) equipped_costumes[slot] = null costume_expired.emit(slot, iname) costume_unequipped.emit(slot) return expired_list