Files
mtgodot-poc/project/security_lock_system.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight
  - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程
  - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题
  - 新增 test_bridge_height_parity.gd 自动化对拍测试
- 40250 怪物击杀经验动效:
  - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附
- 40250 客户端全系统功能对齐(Batches 1-31):
  - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试
- 文档沉淀:
  - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

224 lines
5.7 KiB
GDScript

# security_lock_system.gd —— Metin2 40250 仓库 6 位安全密码与贵重装备防丢锁定 1:1
# 对照 40250 服务端 safebox.cpp, input_login.cpp, char.cpp
class_name SecurityLockSystem
extends RefCounted
signal safebox_unlocked()
signal safebox_closed()
signal password_changed(new_pwd: String)
signal item_locked(item_name: String)
signal item_unlocked(item_name: String)
const DEFAULT_SAFEBOX_PASSWORD := "000000"
const PASSWORD_LEN := 6
const CHAR_DELETE_CODE_LEN := 7
const MAX_FAILED_ATTEMPTS := 5
var safebox_password: String = DEFAULT_SAFEBOX_PASSWORD
var is_safebox_unlocked: bool = false
var failed_attempts: int = 0
var is_locked_out: bool = false
var char_delete_code: String = "1234567"
# 验证并开启仓库
func open_safebox(input_pwd: String) -> Dictionary:
if is_locked_out:
return {
"ok": false,
"reason": "LOCKOUT",
"msg": "连续输错5次密码,仓库已被暂时锁定!请稍后再试。"
}
if input_pwd == safebox_password:
failed_attempts = 0
is_safebox_unlocked = true
safebox_unlocked.emit()
return {
"ok": true,
"msg": "仓库密码验证成功,仓库已开启!"
}
failed_attempts += 1
if failed_attempts >= MAX_FAILED_ATTEMPTS:
is_locked_out = true
return {
"ok": false,
"reason": "WRONG_PASSWORD_LOCKOUT",
"failed_attempts": failed_attempts,
"msg": "密码错误达到上限(5次),仓库已被安全保护锁定!"
}
var remaining := MAX_FAILED_ATTEMPTS - failed_attempts
return {
"ok": false,
"reason": "WRONG_PASSWORD",
"failed_attempts": failed_attempts,
"remaining_attempts": remaining,
"msg": "仓库密码错误!还剩 %d 次尝试机会。" % remaining
}
# 关闭仓库
func close_safebox() -> void:
is_safebox_unlocked = false
safebox_closed.emit()
# 重置防爆保护锁定 (如由 GM 或系统定时重置)
func reset_lockout() -> void:
is_locked_out = false
failed_attempts = 0
# 修改仓库 6 位密码
func change_safebox_password(old_pwd: String, new_pwd: String) -> Dictionary:
if old_pwd != safebox_password:
return {
"ok": false,
"reason": "WRONG_OLD_PASSWORD",
"msg": "旧密码验证失败,无法修改!"
}
if new_pwd.length() != PASSWORD_LEN or not new_pwd.is_valid_int() or int(new_pwd) < 0:
return {
"ok": false,
"reason": "INVALID_PASSWORD_FORMAT",
"msg": "仓库密码必须为严格 6 位纯数字!"
}
if new_pwd == old_pwd:
return {
"ok": false,
"reason": "SAME_PASSWORD",
"msg": "新密码不能与旧密码相同!"
}
safebox_password = new_pwd
password_changed.emit(new_pwd)
return {
"ok": true,
"new_password": new_pwd,
"msg": "仓库密码已成功修改!"
}
# 锁定装备/贵重物品 (防丢锁定)
func lock_item(item: Dictionary, pwd: String) -> Dictionary:
if pwd != safebox_password:
return {
"ok": false,
"reason": "WRONG_SECURITY_PASSWORD",
"msg": "安全密码错误,无法加锁!"
}
if item.get("is_locked", false):
return {
"ok": false,
"reason": "ALREADY_LOCKED",
"msg": "该物品已处于锁定状态!"
}
item["is_locked"] = true
var iname = item.get("name", "物品")
item_locked.emit(iname)
return {
"ok": true,
"item": item,
"msg": "物品【%s】已安全加锁!" % iname
}
# 解除物品锁定
func unlock_item(item: Dictionary, pwd: String) -> Dictionary:
if pwd != safebox_password:
return {
"ok": false,
"reason": "WRONG_SECURITY_PASSWORD",
"msg": "安全密码错误,无法解锁!"
}
if not item.get("is_locked", false):
return {
"ok": false,
"reason": "NOT_LOCKED",
"msg": "该物品未处于锁定状态!"
}
item["is_locked"] = false
var iname = item.get("name", "物品")
item_unlocked.emit(iname)
return {
"ok": true,
"item": item,
"msg": "物品【%s】已解除锁定!" % iname
}
# 动作权限校验 —— 丢弃到地面
func can_drop_item(item: Dictionary) -> Dictionary:
if item.get("is_locked", false):
return {
"allowed": false,
"reason": "ITEM_LOCKED",
"msg": "已加锁的贵重物品无法丢弃!"
}
return {"allowed": true}
# 动作权限校验 —— 出售给 NPC
func can_sell_item(item: Dictionary) -> Dictionary:
if item.get("is_locked", false):
return {
"allowed": false,
"reason": "ITEM_LOCKED",
"msg": "已加锁的贵重物品无法出售给商人!"
}
return {"allowed": true}
# 动作权限校验 —— 彻底销毁
func can_destroy_item(item: Dictionary) -> Dictionary:
if item.get("is_locked", false):
return {
"allowed": false,
"reason": "ITEM_LOCKED",
"msg": "已加锁的贵重物品无法直接销毁!"
}
return {"allowed": true}
# 动作权限校验 —— 玩家交易
func can_trade_item(item: Dictionary) -> Dictionary:
if item.get("is_locked", false):
return {
"allowed": false,
"reason": "ITEM_LOCKED",
"msg": "已加锁的贵重物品无法用于玩家交易!"
}
return {"allowed": true}
# 动作权限校验 —— 强化或熔炼消耗
func can_refine_item(item: Dictionary) -> Dictionary:
if item.get("is_locked", false):
return {
"allowed": false,
"reason": "ITEM_LOCKED",
"msg": "已加锁的装备无法用于强化或附魔消耗!"
}
return {"allowed": true}
# 角色删除 7 位安全码验证 (对照 40250 input_login.cpp)
func verify_character_deletion(input_code: String) -> Dictionary:
if input_code.length() != CHAR_DELETE_CODE_LEN or not input_code.is_valid_int() or int(input_code) < 0:
return {
"ok": false,
"reason": "INVALID_CODE_FORMAT",
"msg": "角色删除码必须为严格 7 位纯数字!"
}
if input_code != char_delete_code:
return {
"ok": false,
"reason": "WRONG_DELETE_CODE",
"msg": "角色删除安全码错误!"
}
return {
"ok": true,
"msg": "角色删除安全码验证通过,允许删除角色。"
}