完善客户端功能并同步差距文档

This commit is contained in:
shenlei
2026-09-03 18:40:01 +09:00
parent f93a172296
commit 13ccb8d02d
135 changed files with 21881 additions and 1259 deletions
+234
View File
@@ -0,0 +1,234 @@
# equip_rules.gd —— 装备穿戴合法性校验(纯函数校验器)。
#
# 语义顺序对齐服务器 `char_item.cpp::CanEquipNow`
# antiflag(性别 / 职业 / 帝国) → wearable_flag 与目标 wear 槽匹配 → limit(等级 / 属性)。
#
# 参考枚举 1:1 自 `REF/GameLib/ItemData.h``EItemAntiFlag` / `EItemWearableFlag` /
# `EWearPositions` / `ELimitTypes` / `EItemType` / `EArmorSubTypes` / `ECostumeSubTypes`)。
# 40250 客户端本身**不做** `CanEquipNow` 前置门(`PythonItemModule` 只暴露 `IsWearableFlag` /
# `GetLimit`,真正的仲裁在服务器)。这里按 §3.9 `__CanShot` 的方式在客户端防御式重建一道前置
# 门——精确的 tie-break 顺序、PCBANG / REAL_TIME 类 limit、已穿同槽替换等留待接服务器比对(seam)。
extends RefCounted
# --- EItemAntiFlagItemData.hclassic 排列:帝国位在 1<<9..1<<11---
const ANTIFLAG_FEMALE := 1 << 0
const ANTIFLAG_MALE := 1 << 1
const ANTIFLAG_WARRIOR := 1 << 2
const ANTIFLAG_ASSASSIN := 1 << 3
const ANTIFLAG_SURA := 1 << 4
const ANTIFLAG_SHAMAN := 1 << 5
const ANTIFLAG_EMPIRE_A := 1 << 9
const ANTIFLAG_EMPIRE_B := 1 << 10
const ANTIFLAG_EMPIRE_R := 1 << 11
# --- EItemWearableFlag ---
const WEARABLE_BODY := 1 << 0
const WEARABLE_HEAD := 1 << 1
const WEARABLE_FOOTS := 1 << 2
const WEARABLE_WRIST := 1 << 3
const WEARABLE_WEAPON := 1 << 4
const WEARABLE_NECK := 1 << 5
const WEARABLE_EAR := 1 << 6
const WEARABLE_UNIQUE := 1 << 7
const WEARABLE_SHIELD := 1 << 8
const WEARABLE_ARROW := 1 << 9
# --- EWearPositions0..10+ 新装备系统槽(GameType.h19..23---
const WEAR_BODY := 0
const WEAR_HEAD := 1
const WEAR_FOOTS := 2
const WEAR_WRIST := 3
const WEAR_WEAPON := 4
const WEAR_NECK := 5
const WEAR_EAR := 6
const WEAR_UNIQUE1 := 7
const WEAR_UNIQUE2 := 8
const WEAR_ARROW := 9
const WEAR_SHIELD := 10
const WEAR_MAX_NUM := 11
const WEAR_COSTUME_BODY := 19
const WEAR_COSTUME_HAIR := 20
const WEAR_RING1 := 21
const WEAR_RING2 := 22
const WEAR_BELT := 23
# --- ELimitTypes ---
const LIMIT_NONE := 0
const LIMIT_LEVEL := 1
const LIMIT_STR := 2
const LIMIT_DEX := 3
const LIMIT_INT := 4
const LIMIT_CON := 5
const LIMIT_PCBANG := 6
const LIMIT_REAL_TIME := 7
# --- EItemType(相关项)+ 子类 ---
const ITEM_TYPE_WEAPON := 1
const ITEM_TYPE_ARMOR := 2
const ITEM_TYPE_COSTUME := 28
const ITEM_TYPE_RING := 33
const ITEM_TYPE_BELT := 34
const ARMOR_BODY := 0
const ARMOR_HEAD := 1
const ARMOR_SHIELD := 2
const ARMOR_WRIST := 3
const ARMOR_FOOTS := 4
const ARMOR_NECK := 5
const ARMOR_EAR := 6
const COSTUME_BODY := 0
const COSTUME_HAIR := 1
# --- 职业 / 性别 <- racecommon length.h `MAIN_RACE_*`job = race % 4;性别见下表)---
const JOB_WARRIOR := 0
const JOB_ASSASSIN := 1
const JOB_SURA := 2
const JOB_SHAMAN := 3
const SEX_MALE := 0
const SEX_FEMALE := 1
# race 0..7 = WARRIOR_M / ASSASSIN_W / SURA_M / SHAMAN_W / WARRIOR_W / ASSASSIN_M / SURA_W / SHAMAN_M
const SEX_BY_RACE := [SEX_MALE, SEX_FEMALE, SEX_MALE, SEX_FEMALE, SEX_FEMALE, SEX_MALE, SEX_FEMALE, SEX_MALE]
const JOB_ANTIFLAG := [ANTIFLAG_WARRIOR, ANTIFLAG_ASSASSIN, ANTIFLAG_SURA, ANTIFLAG_SHAMAN]
# 帝国 1/2/3GC_EMPIRE)→ 对应 anti 位
const EMPIRE_ANTIFLAG := {1: ANTIFLAG_EMPIRE_A, 2: ANTIFLAG_EMPIRE_B, 3: ANTIFLAG_EMPIRE_R}
static func sex_of_race(race: int) -> int:
return int(SEX_BY_RACE[race]) if race >= 0 and race < SEX_BY_RACE.size() else SEX_MALE
static func job_of_race(race: int) -> int:
return (race % 4) if race >= 0 else JOB_WARRIOR
# 物品默认落在哪个 wear 槽(type / sub_type 决定)。不可穿戴 → -1。
static func default_wear_for(item: Dictionary) -> int:
var itype := int(item.get("type", -1))
var sub := int(item.get("sub_type", -1))
match itype:
ITEM_TYPE_WEAPON:
return WEAR_WEAPON
ITEM_TYPE_COSTUME:
return WEAR_COSTUME_HAIR if sub == COSTUME_HAIR else WEAR_COSTUME_BODY
ITEM_TYPE_RING:
return WEAR_RING1
ITEM_TYPE_BELT:
return WEAR_BELT
ITEM_TYPE_ARMOR:
match sub:
ARMOR_HEAD: return WEAR_HEAD
ARMOR_SHIELD: return WEAR_SHIELD
ARMOR_WRIST: return WEAR_WRIST
ARMOR_FOOTS: return WEAR_FOOTS
ARMOR_NECK: return WEAR_NECK
ARMOR_EAR: return WEAR_EAR
_: return WEAR_BODY
return -1
static func is_equippable(item: Dictionary) -> bool:
return default_wear_for(item) >= 0
# wear 槽 → 需要的 wearable_flag 位(新装备槽 19..23 无 wearable_flag → 0)。
static func wear_to_wearable(wear: int) -> int:
match wear:
WEAR_BODY: return WEARABLE_BODY
WEAR_HEAD: return WEARABLE_HEAD
WEAR_FOOTS: return WEARABLE_FOOTS
WEAR_WRIST: return WEARABLE_WRIST
WEAR_WEAPON: return WEARABLE_WEAPON
WEAR_NECK: return WEARABLE_NECK
WEAR_EAR: return WEARABLE_EAR
WEAR_UNIQUE1, WEAR_UNIQUE2: return WEARABLE_UNIQUE
WEAR_ARROW: return WEARABLE_ARROW
WEAR_SHIELD: return WEARABLE_SHIELD
return 0
# 目标槽能否接受这件物品:新装备槽按 item type,旧 11 槽按 wearable_flag 位。
static func slot_accepts(item: Dictionary, wear: int) -> bool:
var itype := int(item.get("type", -1))
match wear:
WEAR_COSTUME_BODY, WEAR_COSTUME_HAIR:
return itype == ITEM_TYPE_COSTUME
WEAR_RING1, WEAR_RING2:
return itype == ITEM_TYPE_RING
WEAR_BELT:
return itype == ITEM_TYPE_BELT
var need := wear_to_wearable(wear)
if need == 0:
return false
return (int(item.get("wear_flags", 0)) & need) != 0
static func _fail(code: String, need := 0) -> Dictionary:
return {"ok": false, "code": code, "need": need}
# 主校验。
# item : proto 字典 —— {type, sub_type, anti_flags, wear_flags, limits:[{type,value}]}
# wear : 目标 EWearPositions(或新装备槽 19..23);传 -1 用 default_wear_for(item)
# ctx : {race, sex, job, empire, level, st, dx, ht, iq}
# —— sex/job 缺省时由 race 推;属性 st/dx/ht/iq 缺省或 < 0 = 不判(seam
# 返回 {ok: bool, code: String, need: int}。code 见下方常量注释;need = 失败 limit 的要求值。
static func can_equip(item: Dictionary, wear: int, ctx: Dictionary) -> Dictionary:
if item.is_empty():
return _fail("NO_ITEM")
var target := wear if wear >= 0 else default_wear_for(item)
if target < 0:
return _fail("NOT_EQUIPPABLE")
var anti := int(item.get("anti_flags", 0))
var race := int(ctx.get("race", 0))
var sex := int(ctx.get("sex", sex_of_race(race)))
var job := int(ctx.get("job", job_of_race(race)))
var empire := int(ctx.get("empire", 0))
# 1) anti-flag:性别
if sex == SEX_FEMALE and (anti & ANTIFLAG_FEMALE):
return _fail("ANTI_SEX")
if sex == SEX_MALE and (anti & ANTIFLAG_MALE):
return _fail("ANTI_SEX")
# 2) anti-flag:职业
if job >= 0 and job < JOB_ANTIFLAG.size() and (anti & int(JOB_ANTIFLAG[job])):
return _fail("ANTI_JOB")
# 3) anti-flag:帝国
var emp_bit := int(EMPIRE_ANTIFLAG.get(empire, 0))
if emp_bit != 0 and (anti & emp_bit):
return _fail("ANTI_EMPIRE")
# 4) wearable_flag 与目标槽匹配
if not slot_accepts(item, target):
return _fail("NOT_WEARABLE_HERE")
# 5) limit(等级 / 属性)—— 逐条 GetLimit
for lim in item.get("limits", []):
var t := int(lim.get("type", 0))
var v := int(lim.get("value", 0))
if v <= 0:
continue
match t:
LIMIT_LEVEL:
if int(ctx.get("level", 0)) < v:
return _fail("LIMIT_LEVEL", v)
LIMIT_STR:
if _stat(ctx, "st") >= 0 and _stat(ctx, "st") < v:
return _fail("LIMIT_STR", v)
LIMIT_DEX:
if _stat(ctx, "dx") >= 0 and _stat(ctx, "dx") < v:
return _fail("LIMIT_DEX", v)
LIMIT_INT:
if _stat(ctx, "iq") >= 0 and _stat(ctx, "iq") < v:
return _fail("LIMIT_INT", v)
LIMIT_CON:
if _stat(ctx, "ht") >= 0 and _stat(ctx, "ht") < v:
return _fail("LIMIT_CON", v)
_:
pass # PCBANG / REAL_TIME* → 服务器判(seam
return {"ok": true, "code": "", "need": 0}
static func _stat(ctx: Dictionary, key: String) -> int:
return int(ctx.get(key, -1))