# soulbound_lock_security_system.gd —— Metin2 40250 神装灵魂绑定与防丢安全锁系统 1:1 # 对照 40250 服务端 char_item.cpp, item.cpp, item_manager.cpp class_name SoulboundLockSecuritySystem extends RefCounted signal item_soulbound(slot: int, item_name: String) signal unbinding_started(slot: int, item_name: String, duration_seconds: float) signal unbinding_cancelled(slot: int, item_name: String) signal item_unbound(slot: int, item_name: String) const VNUM_SOULBIND_SCROLL := 72051 # 灵魂绑定卷轴 const VNUM_SOULUNBIND_SCROLL := 72052 # 灵魂解绑卷轴 const DEFAULT_UNBIND_HOURS := 72.0 # 默认 72 小时解绑安全确认期 # 判定物品是否可在红名死亡时掉落 (灵魂绑定绝对免疫掉落) static func can_drop_on_death(item: Dictionary) -> bool: if item.is_empty(): return false if item.get("is_soulbound", false): return false return true # 判定物品是否允许丢弃至地面 static func can_drop_to_ground(item: Dictionary) -> bool: if item.is_empty(): return false if item.get("is_soulbound", false): return false return true # 判定物品是否允许出售给商店 static func can_sell_to_shop(item: Dictionary) -> bool: if item.is_empty(): return false if item.get("is_soulbound", false): return false return true # 判定物品是否允许放入摆摊货架或交易 static func can_trade_or_stall(item: Dictionary) -> bool: if item.is_empty(): return false if item.get("is_soulbound", false): return false return true # 为装备施加灵魂绑定 (72051) func bind_item(scroll_slot: int, target_slot: int, inventory: Array) -> Dictionary: if scroll_slot < 0 or scroll_slot >= inventory.size() or inventory[scroll_slot] == null: return {"ok": false, "reason": "INVALID_SCROLL_SLOT"} if target_slot < 0 or target_slot >= inventory.size() or inventory[target_slot] == null: return {"ok": false, "reason": "INVALID_TARGET_SLOT"} var scroll = inventory[scroll_slot] var target = inventory[target_slot] if int(scroll.get("vnum", 0)) != VNUM_SOULBIND_SCROLL: return {"ok": false, "reason": "NOT_A_BIND_SCROLL", "msg": "此物品不是【灵魂绑定卷轴】!"} if target.get("is_soulbound", false): return {"ok": false, "reason": "ALREADY_SOULBOUND", "msg": "该装备已处于灵魂绑定保护状态!"} # 消耗 1 个绑定卷轴 var cnt = int(scroll.get("count", 1)) if cnt > 1: scroll["count"] = cnt - 1 else: inventory[scroll_slot] = null target["is_soulbound"] = true target["unbind_remaining_time"] = 0.0 var iname: String = target.get("name", "神装") item_soulbound.emit(target_slot, iname) return { "ok": true, "slot": target_slot, "item_name": iname, "msg": "【%s】已成功铭刻神龙灵魂烙印!永不掉落、永不遗失!" % iname } # 发起解绑倒计时 (72052) func start_unbinding(scroll_slot: int, target_slot: int, inventory: Array, duration_hours: float = DEFAULT_UNBIND_HOURS) -> Dictionary: if scroll_slot < 0 or scroll_slot >= inventory.size() or inventory[scroll_slot] == null: return {"ok": false, "reason": "INVALID_SCROLL_SLOT"} if target_slot < 0 or target_slot >= inventory.size() or inventory[target_slot] == null: return {"ok": false, "reason": "INVALID_TARGET_SLOT"} var scroll = inventory[scroll_slot] var target = inventory[target_slot] if int(scroll.get("vnum", 0)) != VNUM_SOULUNBIND_SCROLL: return {"ok": false, "reason": "NOT_AN_UNBIND_SCROLL", "msg": "此物品不是【灵魂解绑卷轴】!"} if not target.get("is_soulbound", false): return {"ok": false, "reason": "NOT_SOULBOUND", "msg": "该装备尚未绑定,无需解绑!"} if float(target.get("unbind_remaining_time", 0.0)) > 0.0: return {"ok": false, "reason": "UNBIND_ALREADY_IN_PROGRESS", "msg": "该装备已在解绑倒计时中!"} # 消耗解绑卷轴 var cnt = int(scroll.get("count", 1)) if cnt > 1: scroll["count"] = cnt - 1 else: inventory[scroll_slot] = null var duration_seconds = duration_hours * 3600.0 target["unbind_remaining_time"] = duration_seconds var iname: String = target.get("name", "神装") unbinding_started.emit(target_slot, iname, duration_seconds) return { "ok": true, "slot": target_slot, "item_name": iname, "remaining_seconds": duration_seconds, "msg": "【%s】进入解绑安全期,倒计时 %.1f 小时!在此期间随时可取消。" % [iname, duration_hours] } # 取消解绑 (防误操作与防盗号保护) func cancel_unbinding(target_slot: int, inventory: Array) -> Dictionary: if target_slot < 0 or target_slot >= inventory.size() or inventory[target_slot] == null: return {"ok": false, "reason": "INVALID_SLOT"} var target = inventory[target_slot] if float(target.get("unbind_remaining_time", 0.0)) <= 0.0: return {"ok": false, "reason": "NO_UNBIND_IN_PROGRESS", "msg": "该装备并未处于解绑流程中。"} target["unbind_remaining_time"] = 0.0 var iname: String = target.get("name", "神装") unbinding_cancelled.emit(target_slot, iname) return { "ok": true, "msg": "已终止【%s】的解绑流程,灵魂印记完好无损!" % iname } # 倒计时结束完成解绑 func finalize_unbinding(target_slot: int, inventory: Array) -> Dictionary: if target_slot < 0 or target_slot >= inventory.size() or inventory[target_slot] == null: return {"ok": false, "reason": "INVALID_SLOT"} var target = inventory[target_slot] if not target.get("is_soulbound", false): return {"ok": false, "reason": "NOT_SOULBOUND"} if float(target.get("unbind_remaining_time", 1.0)) > 0.0: return {"ok": false, "reason": "COOLDOWN_NOT_FINISHED", "msg": "解绑安全冷却期尚未结束!"} target["is_soulbound"] = false target["unbind_remaining_time"] = 0.0 var iname: String = target.get("name", "神装") item_unbound.emit(target_slot, iname) return { "ok": true, "msg": "【%s】灵魂烙印消散,已成功解绑!" % iname } # 帧心跳更新解绑倒计时 func update(delta: float, inventory: Array) -> void: for item in inventory: if item != null and item.get("is_soulbound", false): var rem = float(item.get("unbind_remaining_time", 0.0)) if rem > 0.0: item["unbind_remaining_time"] = max(0.0, rem - delta)