# 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 # --- EItemAntiFlag(ItemData.h,classic 排列:帝国位在 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 # --- EWearPositions(0..10)+ 新装备系统槽(GameType.h,19..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 # --- 职业 / 性别 <- race(common 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/3(GC_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))