167 lines
7.9 KiB
GDScript
167 lines
7.9 KiB
GDScript
# 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")
|