Files
mtgodot-poc/project/entity_rules_test.gd
T

173 lines
8.2 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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")
_ck(R._is_wooden_door({"kind": R.KIND_WOODEN_DOOR, "race": 999}),
"explicit WOODEN_DOOR kind -> IsWoodenDoor")
_ck(R._is_building({"kind": R.KIND_BUILDING}),
"explicit BUILDING kind -> IsBuilding")
_ck(R._is_building({"kind": R.KIND_WOODEN_DOOR}),
"wooden door kind -> IsBuilding")
# 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")