# costume_attr_transfer_system.gd —— Metin2 40250 官方时装属性转移与增益附魔系统 1:1 # 100% 对照 40250 服务端 char_item.cpp (ITEM_COSTUME, 70063, 70064), item_proto.txt class_name CostumeAttrTransferSystem extends RefCounted signal costume_transformed(costume_vnum: int, new_attrs: Array) signal costume_enchanted(costume_vnum: int, new_attrs: Array) signal costume_attributes_transferred(src_vnum: int, dst_vnum: int, attrs: Array) signal costume_error(reason: String) # 40250 官方时装重铸与附魔道具 Vnum const VNUM_COSTUME_TRANSFORM := 70063 # 时装重铸卷轴 (Kostümü dönüştür - 随机 1~3 条新属性) const VNUM_COSTUME_ENCHANT := 70064 # 时装附魔石 (Kostümü efsunla - 保留条数重洗属性) const VNUM_ATTR_TRANSFER := 72325 # 时装属性转移符 # 时装最大词条数 const MAX_COSTUME_ATTRS := 3 # 40250 官方时装属性词条候选池 const COSTUME_ATTR_POOL: Array[Dictionary] = [ {"type": "max_hp", "name": "生命上限", "min": 500, "max": 2000}, {"type": "max_sp", "name": "法力上限", "min": 200, "max": 1000}, {"type": "crit_pct", "name": "暴击几率 (%)", "min": 5, "max": 10}, {"type": "penetrate_pct", "name": "穿刺伤害几率 (%)", "min": 5, "max": 10}, {"type": "att_speed", "name": "攻击速度", "min": 5, "max": 10}, {"type": "mov_speed", "name": "移动速度", "min": 5, "max": 10}, {"type": "cast_speed", "name": "施法速度", "min": 5, "max": 10}, {"type": "hp_regen", "name": "生命自然恢复 (%)", "min": 10, "max": 30} ] # 校验是否为时装物品 func is_costume_item(item: Dictionary) -> bool: if item == null: return false var subtype = str(item.get("costume_subtype", "")) var is_costume = bool(item.get("is_costume", false)) var vnum = int(item.get("vnum", 0)) # 40250 官方区间:时装铠甲 (41001~44000), 时装发型 (45001~46000), 时装武器 (40101~) if is_costume or subtype != "": return true if (vnum >= 41001 and vnum <= 46000) or (vnum >= 40101 and vnum <= 40999): return true return false # ========================================== # 1. 时装重铸 (70063 Kostümü dönüştür) # ========================================== # 移除所有既有属性,随机赋予 1~3 条全新的时装属性 func transform_costume( inventory: Array, scroll_slot: int, costume_slot: int, forced_count: int = -1, forced_attrs: Array = [] ) -> Dictionary: if not _validate_slots(inventory, scroll_slot, costume_slot): costume_error.emit("INVALID_SLOT") return {"ok": false, "reason": "INVALID_SLOT", "msg": "格位无效或道具为空!"} var scroll = inventory[scroll_slot] var costume = inventory[costume_slot] if int(scroll.get("vnum", 0)) != VNUM_COSTUME_TRANSFORM: costume_error.emit("NOT_TRANSFORM_SCROLL") return {"ok": false, "reason": "NOT_TRANSFORM_SCROLL", "msg": "该道具不是时装重铸卷轴!"} if not is_costume_item(costume): costume_error.emit("NOT_COSTUME") return {"ok": false, "reason": "NOT_COSTUME", "msg": "只有时装物品可以使用时装重铸卷轴!"} if bool(costume.get("is_equipped", false)): costume_error.emit("EQUIPPED_ITEM") return {"ok": false, "reason": "EQUIPPED_ITEM", "msg": "无法对穿戴中的时装进行重铸,请先卸下!"} # 消耗 1 个重铸卷轴 _consume_item_one(inventory, scroll_slot) # 决定新属性条数 (1~3 条) var count = forced_count if forced_count in [1, 2, 3] else randi_range(1, 3) var new_attrs: Array = [] if not forced_attrs.is_empty(): new_attrs = forced_attrs.duplicate(true) else: new_attrs = _roll_random_attributes(count) costume["attrs"] = new_attrs costume_transformed.emit(int(costume.get("vnum", 0)), new_attrs) return { "ok": true, "attrs_count": new_attrs.size(), "attrs": new_attrs, "msg": "时装周身流光溢彩!重铸成功,获得了 %d 条全新的时装属性。" % new_attrs.size() } # ========================================== # 2. 时装附魔 (70064 Kostümü efsunla) # ========================================== # 保留当前属性条数,重新随机洗练词条类型与数值 func enchant_costume( inventory: Array, scroll_slot: int, costume_slot: int, forced_attrs: Array = [] ) -> Dictionary: if not _validate_slots(inventory, scroll_slot, costume_slot): costume_error.emit("INVALID_SLOT") return {"ok": false, "reason": "INVALID_SLOT", "msg": "格位无效或道具为空!"} var scroll = inventory[scroll_slot] var costume = inventory[costume_slot] if int(scroll.get("vnum", 0)) != VNUM_COSTUME_ENCHANT: costume_error.emit("NOT_ENCHANT_SCROLL") return {"ok": false, "reason": "NOT_ENCHANT_SCROLL", "msg": "该道具不是时装附魔石!"} if not is_costume_item(costume): costume_error.emit("NOT_COSTUME") return {"ok": false, "reason": "NOT_COSTUME", "msg": "只有时装物品可以使用时装附魔石!"} if bool(costume.get("is_equipped", false)): costume_error.emit("EQUIPPED_ITEM") return {"ok": false, "reason": "EQUIPPED_ITEM", "msg": "无法对穿戴中的时装进行附魔,请先卸下!"} var existing_attrs: Array = costume.get("attrs", []) if existing_attrs.is_empty(): costume_error.emit("NO_ATTRS_TO_ENCHANT") return {"ok": false, "reason": "NO_ATTRS_TO_ENCHANT", "msg": "此时装没有任何附加属性!请先使用时装重铸卷轴赋予属性。"} # 消耗 1 个附魔石 _consume_item_one(inventory, scroll_slot) var count = existing_attrs.size() var new_attrs: Array = [] if not forced_attrs.is_empty(): new_attrs = forced_attrs.duplicate(true) else: new_attrs = _roll_random_attributes(count) costume["attrs"] = new_attrs costume_enchanted.emit(int(costume.get("vnum", 0)), new_attrs) return { "ok": true, "attrs_count": new_attrs.size(), "attrs": new_attrs, "msg": "附魔灵石融入织物!保留原有 %d 条属性槽位,洗练出了全新的强力词条。" % count } # ========================================== # 3. 时装属性转移 (Costume Attribute Transfer) # ========================================== func transfer_costume_attributes( inventory: Array, scroll_slot: int, src_slot: int, dst_slot: int ) -> Dictionary: if scroll_slot < 0 or scroll_slot >= inventory.size() or inventory[scroll_slot] == null: costume_error.emit("INVALID_SLOT") return {"ok": false, "reason": "INVALID_SLOT", "msg": "转移卷轴格位无效!"} if src_slot < 0 or src_slot >= inventory.size() or inventory[src_slot] == null: costume_error.emit("INVALID_SLOT") return {"ok": false, "reason": "INVALID_SLOT", "msg": "源时装格位无效!"} if dst_slot < 0 or dst_slot >= inventory.size() or inventory[dst_slot] == null: costume_error.emit("INVALID_SLOT") return {"ok": false, "reason": "INVALID_SLOT", "msg": "目标时装格位无效!"} var scroll = inventory[scroll_slot] var src_item = inventory[src_slot] var dst_item = inventory[dst_slot] if int(scroll.get("vnum", 0)) != VNUM_ATTR_TRANSFER: costume_error.emit("NOT_TRANSFER_SCROLL") return {"ok": false, "reason": "NOT_TRANSFER_SCROLL", "msg": "该物品不是时装属性转移符!"} if not is_costume_item(src_item) or not is_costume_item(dst_item): costume_error.emit("NOT_COSTUME") return {"ok": false, "reason": "NOT_COSTUME", "msg": "源道具与目标道具都必须是时装!"} var src_type = str(src_item.get("costume_subtype", "body")) var dst_type = str(dst_item.get("costume_subtype", "body")) if src_type != dst_type: costume_error.emit("TYPE_MISMATCH") return {"ok": false, "reason": "TYPE_MISMATCH", "msg": "时装类型不匹配!时装铠甲只能转移至时装铠甲,发型只能转移至发型。"} var src_attrs: Array = src_item.get("attrs", []) if src_attrs.is_empty(): costume_error.emit("NO_ATTRS_TO_TRANSFER") return {"ok": false, "reason": "NO_ATTRS_TO_TRANSFER", "msg": "源时装没有任何可转移的属性!"} # 消耗 1 张转移符 _consume_item_one(inventory, scroll_slot) # 执行无损属性迁移 var transferred = src_attrs.duplicate(true) dst_item["attrs"] = transferred src_item["attrs"] = [] # 源时装词条清空 costume_attributes_transferred.emit(int(src_item.get("vnum", 0)), int(dst_item.get("vnum", 0)), transferred) return { "ok": true, "transferred_attrs": transferred, "msg": "乾坤倒转!成功将 %d 条极品属性从旧时装完整迁移至新时装。" % transferred.size() } # 内部随机属性抽取工具 func _roll_random_attributes(count: int) -> Array: var result: Array = [] var pool_indices: Array[int] = [] for i in range(COSTUME_ATTR_POOL.size()): pool_indices.append(i) pool_indices.shuffle() var actual_count = min(count, pool_indices.size()) for i in range(actual_count): var def = COSTUME_ATTR_POOL[pool_indices[i]] var val = randi_range(int(def["min"]), int(def["max"])) result.append({ "type": def["type"], "name": def["name"], "value": val }) return result func _validate_slots(inventory: Array, s1: int, s2: int) -> bool: if s1 < 0 or s1 >= inventory.size() or inventory[s1] == null: return false if s2 < 0 or s2 >= inventory.size() or inventory[s2] == null: return false return true func _consume_item_one(inventory: Array, slot: int) -> void: if slot < 0 or slot >= inventory.size() or inventory[slot] == null: return var item = inventory[slot] var count = int(item.get("count", 1)) if count > 1: item["count"] = count - 1 else: inventory[slot] = null