# safebox_storage_keeper_system.gd —— 40250 官方仓库保管员金条熔铸与密码保险箱系统 1:1 # 严格对照: # metin2/src/server/game/src/safebox.h, safebox.cpp # metin2/src/server/game/src/char.cpp:5569-5605 (ReqSafeboxLoad) # metin2/src/server/game/src/input_db.cpp:1225-1250 (SafeboxWrongPassword / SafeboxChangePasswordAnswer) # metin2/src/server/db/src/ClientManager.cpp:515-535 (RESULT_SAFEBOX_LOAD: default "000000") # metin2/src/server/share/conf/item_proto.txt (80003~80007 金银条) extends RefCounted const SAFEBOX_MAX_NUM := 45 # 官方单页保险箱格数 (45 格) const SAFEBOX_PASSWORD_MAX_LEN := 6 # 密码最长 6 位 const DEFAULT_PASSWORD := "000000" # 官方默认初始密码 const SAFEBOX_OPEN_DISTANCE := 1000 # 距离 NPC 9005 最大距离 1000 const SAFEBOX_COOLDOWN_SEC := 10 # 开箱冷却 10 秒 (PASSES_PER_SEC(10)) # 金银条规格表 (Vnum -> { "name": String, "face_value": int, "smelt_cost": int }) # 官方 1% 熔铸税 (cost = face_value * 1.01) const GOLD_BAR_SPECS := { 80003: { "name": "银条 (5万)", "face_value": 50000, "smelt_cost": 50500 }, 80004: { "name": "银条 (10万)", "face_value": 100000, "smelt_cost": 101000 }, 80005: { "name": "金条 (50万)", "face_value": 500000, "smelt_cost": 505000 }, 80006: { "name": "金条 (100万)", "face_value": 1000000, "smelt_cost": 1010000 }, 80007: { "name": "金条 (200万)", "face_value": 2000000, "smelt_cost": 2020000 }, } var password: String = DEFAULT_PASSWORD var is_open: bool = false var last_open_time: int = -100 var current_time: int = 1000 # 保险箱 45 格网格 (slot 0..44 -> Dictionary or null) var safebox_grid: Array = [] func _init() -> void: password = DEFAULT_PASSWORD is_open = false last_open_time = -100 current_time = 1000 safebox_grid.clear() for i in range(SAFEBOX_MAX_NUM): safebox_grid.append(null) # 1. 密码校验并打开保险箱 (1:1 CHARACTER::ReqSafeboxLoad) func open_safebox(entered_pw: String, npc_distance: float = 200.0) -> Dictionary: var res := { "success": false, "error_code": "OK", "message": "" } # 距离检查 if npc_distance > SAFEBOX_OPEN_DISTANCE: res["error_code"] = "TOO_FAR" res["message"] = "<仓库> 距离太远,无法打开仓库。" return res # 10秒防刷频冷却检查 if (current_time - last_open_time) < SAFEBOX_COOLDOWN_SEC: res["error_code"] = "COOLDOWN_ACTIVE" res["message"] = "<仓库> 仓库在 10 秒内只能打开一次。" return res # 密码长度检查 if entered_pw.is_empty() or entered_pw.length() > SAFEBOX_PASSWORD_MAX_LEN: res["error_code"] = "INVALID_PASSWORD_LEN" res["message"] = "<仓库> 密码格式错误,必须为 6 位字符以内。" return res # 密码匹配检查 (ClientManager.cpp:525) if entered_pw != password: res["error_code"] = "WRONG_PASSWORD" res["message"] = "<仓库> 仓库密码错误!" return res # 打开成功 is_open = true last_open_time = current_time res["success"] = true res["message"] = "<仓库> 仓库开启成功。" return res # 2. 修改仓库密码 (1:1 SafeboxChangePassword) func change_password(old_pw: String, new_pw: String) -> Dictionary: var res := { "success": false, "error_code": "OK", "message": "" } if not is_open: res["error_code"] = "SAFEBOX_NOT_OPEN" res["message"] = "<仓库> 必须先开启仓库方可修改密码。" return res if old_pw != password: res["error_code"] = "OLD_PASSWORD_MISMATCH" res["message"] = "<仓库> 旧密码不匹配,修改失败。" return res if new_pw.is_empty() or new_pw.length() > SAFEBOX_PASSWORD_MAX_LEN: res["error_code"] = "INVALID_NEW_PASSWORD" res["message"] = "<仓库> 新密码长度不合法。" return res password = new_pw res["success"] = true res["message"] = "<仓库> 仓库密码已成功更改。" return res # 3. 关闭仓库 func close_safebox() -> void: is_open = false # 4. 存入物品 (Inventory -> Safebox) func deposit_item(safebox_slot: int, item_data: Dictionary) -> Dictionary: var res := { "success": false, "error_code": "OK", "message": "" } if not is_open: res["error_code"] = "NOT_OPEN" return res if safebox_slot < 0 or safebox_slot >= SAFEBOX_MAX_NUM: res["error_code"] = "INVALID_SLOT" return res if safebox_grid[safebox_slot] != null: # 尝试堆叠 var existing: Dictionary = safebox_grid[safebox_slot] if existing.get("vnum") == item_data.get("vnum") and existing.get("stackable", false): existing["count"] = existing.get("count", 1) + item_data.get("count", 1) res["success"] = true res["message"] = "物品成功堆叠存入仓库。" return res else: res["error_code"] = "SLOT_OCCUPIED" res["message"] = "目标仓库格位已被占用。" return res safebox_grid[safebox_slot] = item_data.duplicate(true) res["success"] = true res["message"] = "物品成功存入仓库。" return res # 5. 取出物品 (Safebox -> Inventory) func withdraw_item(safebox_slot: int) -> Dictionary: var res := { "success": false, "item": null, "error_code": "OK" } if not is_open: res["error_code"] = "NOT_OPEN" return res if safebox_slot < 0 or safebox_slot >= SAFEBOX_MAX_NUM: res["error_code"] = "INVALID_SLOT" return res if safebox_grid[safebox_slot] == null: res["error_code"] = "EMPTY_SLOT" return res var it: Dictionary = safebox_grid[safebox_slot] safebox_grid[safebox_slot] = null res["success"] = true res["item"] = it return res # 6. 保险箱内格位互换/移动 func move_safebox_item(from_slot: int, to_slot: int) -> bool: if not is_open: return false if from_slot < 0 or from_slot >= SAFEBOX_MAX_NUM or to_slot < 0 or to_slot >= SAFEBOX_MAX_NUM: return false if safebox_grid[from_slot] == null: return false var item_a = safebox_grid[from_slot] var item_b = safebox_grid[to_slot] if item_b != null and item_b.get("vnum") == item_a.get("vnum") and item_a.get("stackable", false): item_b["count"] = item_b.get("count", 1) + item_a.get("count", 1) safebox_grid[from_slot] = null else: safebox_grid[to_slot] = item_a safebox_grid[from_slot] = item_b return true # 7. 金条/银条熔铸 (Player Gold -> Gold Bar Item) # 仓库保管员按 1% 手续费收取金币并铸造成可储存/交易金条 func smelt_gold_bar(player_gold: int, bar_vnum: int) -> Dictionary: var res := { "success": false, "new_gold": player_gold, "bar_item": null, "cost": 0, "error_code": "OK", "message": "" } if not GOLD_BAR_SPECS.has(bar_vnum): res["error_code"] = "INVALID_BAR_VNUM" res["message"] = "未知的金银条规格。" return res var spec: Dictionary = GOLD_BAR_SPECS[bar_vnum] var cost: int = spec["smelt_cost"] res["cost"] = cost if player_gold < cost: res["error_code"] = "INSUFFICIENT_GOLD" res["message"] = "金币不足,铸造 %s 需支付 %d 金币 (含1%%熔铸税)。" % [spec["name"], cost] return res res["new_gold"] = player_gold - cost res["bar_item"] = { "vnum": bar_vnum, "name": spec["name"], "count": 1, "face_value": spec["face_value"] } res["success"] = true res["message"] = "成功熔铸 %s!扣除 %d 金币。" % [spec["name"], cost] return res # 8. 金条/银条承兑支取现金 (Gold Bar Item -> Player Gold) # 承兑取回票面全额金币 (1:1 face_value) func encash_gold_bar(player_gold: int, bar_vnum: int) -> Dictionary: var res := { "success": false, "new_gold": player_gold, "cash_value": 0, "error_code": "OK", "message": "" } if not GOLD_BAR_SPECS.has(bar_vnum): res["error_code"] = "INVALID_BAR_VNUM" res["message"] = "非官方认证金银条,无法承兑。" return res var spec: Dictionary = GOLD_BAR_SPECS[bar_vnum] var face_val: int = spec["face_value"] res["cash_value"] = face_val res["new_gold"] = player_gold + face_val res["success"] = true res["message"] = "成功承兑 %s,兑现出金币 %d!" % [spec["name"], face_val] return res