# 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 异帝国 PC(else 分支) _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_FREE:victim 非 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_FREE:victim 是 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 victim(state_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 alignment(self 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_instance:victim 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")