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

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
+166
View File
@@ -0,0 +1,166 @@
# entity_rules_test —— §5.4 CInstanceBase::IsAttackableInstance 1:1 迁移自检。
# godot --headless --path project --script entity_rules_test.gd
# 覆盖:alignment grade/type 边界、观战门、自打自、PC/怪/NPC/石头/木门、
# 同帝国 peace、异帝国、PK_FREE/GUILD/REVENGE 分支、killer、决斗三态、
# PVP 关系表、公会战对表、conflict alignment、变身自身。
extends SceneTree
const EntityRules = preload("res://entity_rules.gd")
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _e(kind: int, vid: int, extra: Dictionary = {}) -> Dictionary:
var d := {"kind": kind, "vid": vid, "empire": 1, "alignment": 0, "pk_mode": 0, "guild": 0, "state_flags": 0}
for k in extra:
d[k] = extra[k]
return d
func _ctx(extra: Dictionary = {}) -> Dictionary:
var d := {
"observer": false, "main_vid": 1, "pk_mode": 0, "duel_mode": 0,
"duel_opponents": [], "pvp_pairs": [], "gvg_pairs": [], "party_vids": [],
}
for k in extra:
d[k] = extra[k]
return d
func _init() -> void:
_run()
if _fail == 0:
print("PASS: entity_rules_test (§5.4 IsAttackableInstance 1:1)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
var R = EntityRules
# 1) alignment grade / type 边界(InstanceBase.cpp:502 / :524
_ck(R.alignment_grade(12000) == 0 and R.alignment_grade(11999) == 1, "grade 12000/11999")
_ck(R.alignment_grade(0) == 4 and R.alignment_grade(-1) == 5, "grade 0/-1")
_ck(R.alignment_grade(-12000) == 8 and R.alignment_grade(-11999) == 7, "grade -12000/-11999")
_ck(R.alignment_type(5000) == R.ALIGNMENT_TYPE_WHITE, "type white (grade<=3)")
_ck(R.alignment_type(0) == R.ALIGNMENT_TYPE_NORMAL, "type normal (grade 4)")
_ck(R.alignment_type(-5000) == R.ALIGNMENT_TYPE_DARK, "type dark (grade>=5)")
var me := _e(R.KIND_PC, 1)
# 2) 观战模式:主角一律不可攻击
_ck(not R.is_attackable_instance(me, _e(R.KIND_MONSTER, 2),
_ctx({"observer": true})), "observer -> not attackable")
# 3) 自己打自己
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 1), _ctx()), "self == victim -> false")
# 4) PC vs 怪(IsEnemy
_ck(R.is_attackable_instance(me, _e(R.KIND_MONSTER, 9), _ctx()), "PC vs monster -> true")
# 5) PC vs NPC —— 无分支命中
_ck(not R.is_attackable_instance(me, _e(R.KIND_NPC, 9), _ctx()), "PC vs NPC -> false")
# 6) PC vs 同帝国 peace PC
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 2), _ctx()),
"PC vs same-empire peace PC -> false")
# 7) PC vs 异帝国 PCelse 分支)
_ck(R.is_attackable_instance(me, _e(R.KIND_PC, 2, {"empire": 2}), _ctx()),
"PC vs different-empire PC -> true")
# empire==0 的 victim 视为同帝国(IsSameEmpire 首行)
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 2, {"empire": 0}), _ctx()),
"victim empire 0 -> treated same empire -> false")
# 8) PK_FREEvictim 非 protect 且不同队 -> true
_ck(R.is_attackable_instance(me, _e(R.KIND_PC, 2),
_ctx({"pk_mode": R.PK_MODE_FREE})), "PK_FREE vs normal PC -> true")
# 9) PK_FREEvictim 是 protect -> false
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 2, {"pk_mode": R.PK_MODE_PROTECT}),
_ctx({"pk_mode": R.PK_MODE_FREE})), "PK_FREE vs protect victim -> false")
# 10) PK_FREE:同队 -> false
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 2),
_ctx({"pk_mode": R.PK_MODE_FREE, "party_vids": [1, 2]})),
"PK_FREE same party -> false")
# 11) PK_GUILD:同公会 -> 提前 false
var me_g := _e(R.KIND_PC, 1, {"guild": 7})
_ck(not R.is_attackable_instance(me_g, _e(R.KIND_PC, 2, {"guild": 7}),
_ctx({"pk_mode": R.PK_MODE_GUILD})), "PK_GUILD same guild -> false")
# 12) PK_GUILD:不同公会、victim 非 protect、不同队 -> true
_ck(R.is_attackable_instance(me_g, _e(R.KIND_PC, 2, {"guild": 9}),
_ctx({"pk_mode": R.PK_MODE_GUILD})), "PK_GUILD diff guild -> true")
# 13) killer victimstate_flags bit3):不同队即可打,即便自己 peace
_ck(R.is_attackable_instance(me, _e(R.KIND_PC, 2, {"state_flags": R.STATE_KILLER}), _ctx()),
"killer victim, not party -> true")
# 14) killer victim 但同队 -> false
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 2, {"state_flags": R.STATE_KILLER}),
_ctx({"party_vids": [1, 2]})), "killer victim, same party -> false")
# 15) 决斗 DUEL_CANNOTATTACK -> false
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 2),
_ctx({"duel_mode": R.DUEL_CANNOTATTACK})), "DUEL_CANNOTATTACK -> false")
# 16) 决斗 DUEL_START:对手在名单 -> true;不在 -> false
_ck(R.is_attackable_instance(me, _e(R.KIND_PC, 2),
_ctx({"duel_mode": R.DUEL_START, "duel_opponents": [2]})),
"DUEL_START vs opponent -> true")
_ck(not R.is_attackable_instance(me, _e(R.KIND_PC, 3),
_ctx({"duel_mode": R.DUEL_START, "duel_opponents": [2]})),
"DUEL_START vs non-opponent -> false")
# 17) PVP 关系表(__FindPVPKey):同帝国 peace 也能打
_ck(R.is_attackable_instance(me, _e(R.KIND_PC, 2),
_ctx({"pvp_pairs": [[2, 1]]})), "PVP pair present -> true")
# 18) 公会战对表(__FindGVGKey):双方都有公会且对表命中
_ck(R.is_attackable_instance(me_g, _e(R.KIND_PC, 2, {"guild": 9}),
_ctx({"gvg_pairs": [[7, 9]]})), "GVG pair present -> true")
_ck(not R.is_attackable_instance(me_g, _e(R.KIND_PC, 2, {"guild": 0}),
_ctx({"gvg_pairs": [[7, 0]]})), "GVG needs both guilds non-zero -> false")
# 19) PK_REVENGE + conflict alignmentself white, victim dark)不同队 -> true
var me_w := _e(R.KIND_PC, 1, {"alignment": 10000})
_ck(R.is_attackable_instance(me_w, _e(R.KIND_PC, 2, {"alignment": -10000}),
_ctx({"pk_mode": R.PK_MODE_REVENGE})), "REVENGE + conflict alignment -> true")
# 20) PK_REVENGE 但 alignment 不冲突(都 white-> false
_ck(not R.is_attackable_instance(me_w, _e(R.KIND_PC, 2, {"alignment": 10000}),
_ctx({"pk_mode": R.PK_MODE_REVENGE})), "REVENGE + non-conflict -> false")
# 21) 石头自身 vs PC victim -> true
_ck(R.is_attackable_instance(_e(R.KIND_STONE, 5), _e(R.KIND_PC, 2), _ctx({"main_vid": 5})),
"stone self vs PC -> true")
# 22) PC self vs 石头 victim -> true
_ck(R.is_attackable_instance(me, _e(R.KIND_STONE, 5), _ctx()), "PC vs stone -> true")
# 23) PC vs 木门(race 13000 / 30111..30119
_ck(R.is_attackable_instance(me, {"kind": 9, "vid": 8, "race": 13000}, _ctx()),
"PC vs wooden door 13000 -> true")
_ck(R.is_attackable_instance(me, {"kind": 9, "vid": 8, "race": 30115}, _ctx()),
"PC vs wooden door 30115 -> true")
_ck(not R.is_attackable_instance(me, {"kind": 9, "vid": 8, "race": 13001}, _ctx()),
"PC vs stone door 13001 -> false")
# 24) is_conflict_alignment_instancevictim PK_PROTECT 一律 false
_ck(not R.is_conflict_alignment_instance(me_w,
_e(R.KIND_PC, 2, {"alignment": -10000, "pk_mode": R.PK_MODE_PROTECT})),
"conflict alignment: protect victim -> false")
# dark self vs 非 dark victim -> conflict
var me_d := _e(R.KIND_PC, 1, {"alignment": -10000})
_ck(R.is_conflict_alignment_instance(me_d, _e(R.KIND_PC, 2, {"alignment": 0})),
"conflict alignment: dark vs normal -> true")
_ck(not R.is_conflict_alignment_instance(me_d, _e(R.KIND_PC, 2, {"alignment": -20000})),
"conflict alignment: dark vs dark -> false")
# 25a) 变身玩家(TYPE_PC + 越界 race):IsPC() 仍为真 → 完全走 PC 分支。
var me_polypc := _e(R.KIND_PC, 1, {"poly": true})
_ck(not R.is_attackable_instance(me_polypc, _e(R.KIND_PC, 2), _ctx()),
"polymorphed PC vs peaceful same-empire PC -> false (still a PC)")
_ck(R.is_attackable_instance(me_polypc, _e(R.KIND_MONSTER, 3), _ctx()),
"polymorphed PC vs monster -> true")
# 25b) 真正的 TYPE_POLY 实例(非 PC 类别)走 else-if IsPoly() 分支:vs PC / vs 怪 均可打。
var poly_self := _e(R.KIND_NPC, 1, {"poly": true})
_ck(R.is_attackable_instance(poly_self, _e(R.KIND_PC, 2), _ctx()), "TYPE_POLY self vs PC -> true")
_ck(R.is_attackable_instance(poly_self, _e(R.KIND_MONSTER, 3), _ctx()), "TYPE_POLY self vs enemy -> true")