# entity_rules.gd —— CInstanceBase::IsAttackableInstance 及其判据的 1:1 迁移(§5.4)。 # # 参考(REF/ = /Users/shenlei/Work/mt/40250/ClientVS22/source/): # UserInterface/InstanceBase.cpp:2147 IsAttackableInstance —— 唯一判据,逐行照抄 # UserInterface/InstanceBase.cpp:2116 IsConflictAlignmentInstance # UserInterface/InstanceBase.cpp:470 IsSameEmpire # UserInterface/InstanceBase.cpp:502 GetAlignmentGrade # UserInterface/InstanceBase.cpp:524 GetAlignmentType # UserInterface/InstanceBase.cpp:2059 SetPKMode / :2073 SetKiller / :2086 SetStateFlags # UserInterface/InstanceBaseEffect.cpp:495 __FindDUELKey # UserInterface/InstanceBaseEffect.cpp:505 IsPVPInstance # UserInterface/PythonPlayer.cpp:1445 IsSamePartyMember # UserInterface/InstanceBase.cpp:2390 IsWoodenDoor(vnum 13000 / 30111..30119) # # 纯静态谓词。调用方(net_play.gd)负责把网络快照拼成 self_e / victim_e / ctx; # 部分依赖在本 40250 客户端的发布源码里没有 setter(同 §3.4/§3.6/§3.8 的处理), # 在下方逐条标注为 seam。 extends RefCounted # EPKModes(REF/UserInterface/Packet.h:1273) const PK_MODE_PEACE := 0 const PK_MODE_REVENGE := 1 const PK_MODE_FREE := 2 const PK_MODE_PROTECT := 3 const PK_MODE_GUILD := 4 # alignment type(REF/UserInterface/InstanceBase.h:249) const ALIGNMENT_TYPE_WHITE := 0 const ALIGNMENT_TYPE_NORMAL := 1 const ALIGNMENT_TYPE_DARK := 2 # duel mode(REF/UserInterface/InstanceBase.h:378,GetDuelMode 返回 m_dwDuelMode) const DUEL_NONE := 0 const DUEL_CANNOTATTACK := 1 const DUEL_START := 2 # ADD_CHARACTER_STATE_*(REF/UserInterface/Packet.h:1264)—— SetStateFlags 从 state_flags # 位里取 killer / party(InstanceBase.cpp:2086)。 const STATE_KILLER := 1 << 3 const STATE_PARTY := 1 << 4 # _entity_kind 语义(net_play._entity_kind):0 PC / 1 NPC / 2 MONSTER / 3 STONE / # 4 WARP / 5 BUILDING / 6 WOODEN_DOOR。 const KIND_PC := 0 const KIND_NPC := 1 const KIND_MONSTER := 2 const KIND_STONE := 3 const KIND_WARP := 4 const KIND_BUILDING := 5 const KIND_WOODEN_DOOR := 6 # --- alignment(善恶)--------------------------------------------------------- # CInstanceBase::GetAlignmentGrade(InstanceBase.cpp:502) static func alignment_grade(a: int) -> int: if a >= 12000: return 0 elif a >= 8000: return 1 elif a >= 4000: return 2 elif a >= 1000: return 3 elif a >= 0: return 4 elif a > -4000: return 5 elif a > -8000: return 6 elif a > -12000: return 7 return 8 # CInstanceBase::GetAlignmentType(InstanceBase.cpp:524) static func alignment_type(a: int) -> int: match alignment_grade(a): 0, 1, 2, 3: return ALIGNMENT_TYPE_WHITE 5, 6, 7, 8: return ALIGNMENT_TYPE_DARK return ALIGNMENT_TYPE_NORMAL # --- 类别谓词(GraphicThingInstance 的 type 位,见 net_play._entity_kind)--------- static func _kind(e: Dictionary) -> int: return int(e.get("kind", e.get("ch_type", 0))) static func _is_poly(e: Dictionary) -> bool: # m_GraphicThingInstance.IsPoly()(变身:模型换成怪,thing type 变 ENEMY) return bool(e.get("poly", false)) \ or int(e.get("polymorph", 0)) != 0 \ or int(e.get("poly_vnum", 0)) != 0 static func _is_pc(e: Dictionary) -> bool: # CActorInstance::IsPC = (TYPE_PC == m_eActorType)(ActorInstance.cpp:282)。 # 变身中的玩家仍是 TYPE_PC —— IsPoly() 另经 race >= MAIN_RACE_MAX_NUM 判真, # 但 IsPC() 不看 race,所以这里**不**排除 poly(否则 IsAttackableInstance 的 # `else if (IsPC())` 会漏掉变身玩家,与参考端不一致)。 return _kind(e) == KIND_PC static func _is_stone(e: Dictionary) -> bool: return _kind(e) == KIND_STONE static func _is_enemy(e: Dictionary) -> bool: # CActorInstance::IsEnemy = (TYPE_ENEMY == m_eActorType)(ActorInstance.cpp:298)—— # 仅普通怪。变身玩家是 TYPE_PC 不是 ENEMY;独立的 TYPE_POLY 实例归 _is_poly。 return _kind(e) == KIND_MONSTER static func _is_wooden_door(e: Dictionary) -> bool: # CInstanceBase::IsWoodenDoor(InstanceBase.cpp:2390) if _kind(e) == KIND_WOODEN_DOOR: return true var vnum := int(e.get("race", e.get("vnum", 0))) return vnum == 13000 or (vnum >= 30111 and vnum <= 30119) static func _is_building(e: Dictionary) -> bool: # CInstanceBase::IsBuilding() = m_GraphicThingInstance.IsBuilding()。 # classic 的 bType / POC fixture 可直接给 kind;旧包仍可用 building 布尔字段。 return bool(e.get("building", false)) or _kind(e) == KIND_BUILDING \ or _kind(e) == KIND_WOODEN_DOOR # --- killer / party / empire / duel / pvp ----------------------------------- static func _is_killer(e: Dictionary) -> bool: # m_isKiller,由 SetStateFlags(state_flags & ADD_CHARACTER_STATE_KILLER) 置位。 return (int(e.get("state_flags", 0)) & STATE_KILLER) != 0 static func _is_same_party(a_vid: int, b_vid: int, ctx: Dictionary) -> bool: # CPythonPlayer::IsSamePartyMember = IsPartyMemberByVID(a) && IsPartyMemberByVID(b) var vids: Array = ctx.get("party_vids", []) return a_vid in vids and b_vid in vids static func _is_same_empire(self_e: Dictionary, victim_e: Dictionary) -> bool: # CInstanceBase::IsSameEmpire(InstanceBase.cpp:470) if int(victim_e.get("empire", 0)) == 0: return true # IsGameMaster() —— 本 POC 无 GM 标记 → seam(恒 false)。 if bool(self_e.get("game_master", false)) or bool(victim_e.get("game_master", false)): return true return int(victim_e.get("empire", 0)) == int(self_e.get("empire", 0)) static func _find_duel_key(self_vid: int, victim_vid: int, ctx: Dictionary) -> bool: # CInstanceBase::__FindDUELKey(InstanceBaseEffect.cpp:495)—— g_kSet_dwDUELKey 里 # 有 MAKE_PVPKEY(min,max) 才算。本 POC 的 get_duel().opponents 是主角的对手 VID 列表。 var opp: Array = ctx.get("duel_opponents", []) return victim_vid in opp or self_vid in opp static func _pair_in(pairs: Array, a: int, b: int) -> bool: for p in pairs: if (int(p[0]) == a and int(p[1]) == b) or (int(p[0]) == b and int(p[1]) == a): return true return false static func _is_pvp_instance(self_e: Dictionary, victim_e: Dictionary, ctx: Dictionary) -> bool: # CInstanceBase::IsPVPInstance(InstanceBaseEffect.cpp:505): # if (GetDuelMode()) return true; # return __FindPVPKey(vidSrc, vidDst) || __FindGVGKey(guildSrc, guildDst); if int(ctx.get("duel_mode", 0)) != 0: return true var sv := int(self_e.get("vid", 0)) var vv := int(victim_e.get("vid", 0)) if _pair_in(ctx.get("pvp_pairs", []), sv, vv): return true var sg := int(self_e.get("guild", 0)) var vg := int(victim_e.get("guild", 0)) if sg != 0 and vg != 0 and _pair_in(ctx.get("gvg_pairs", []), sg, vg): return true return false static func is_conflict_alignment_instance(self_e: Dictionary, victim_e: Dictionary) -> bool: # CInstanceBase::IsConflictAlignmentInstance(InstanceBase.cpp:2116) if _pk_mode_of(victim_e) == PK_MODE_PROTECT: return false var mine := alignment_type(int(self_e.get("alignment", 0))) var theirs := alignment_type(int(victim_e.get("alignment", 0))) match mine: ALIGNMENT_TYPE_NORMAL, ALIGNMENT_TYPE_WHITE: if theirs == ALIGNMENT_TYPE_DARK: return true ALIGNMENT_TYPE_DARK: if mine != theirs: return true return false static func _pk_mode_of(e: Dictionary) -> int: return int(e.get("pk_mode", 0)) # --- CInstanceBase::IsAttackableInstance(InstanceBase.cpp:2147)—— 逐行 ----------- static func is_attackable_instance(self_e: Dictionary, victim_e: Dictionary, ctx: Dictionary) -> bool: var sv := int(self_e.get("vid", 0)) var vv := int(victim_e.get("vid", -1)) # if (__IsMainInstance()) { if (rkPlayer.IsObserverMode()) return false; } if sv == int(ctx.get("main_vid", 0)) and bool(ctx.get("observer", false)): return false # if (GetVirtualID() == rkInstVictim.GetVirtualID()) return false; if sv == vv: return false var self_pk := int(ctx.get("pk_mode", 0)) if _is_stone(self_e): if _is_pc(victim_e): return true elif _is_pc(self_e): if _is_stone(victim_e): return true if _is_pc(victim_e): var duel := int(ctx.get("duel_mode", 0)) if duel != 0: match duel: DUEL_CANNOTATTACK: return false DUEL_START: return _find_duel_key(sv, vv, ctx) # if (PK_MODE_GUILD == GetPKMode()) if (GetGuildID() == victim.GetGuildID()) return false; if self_pk == PK_MODE_GUILD: if int(self_e.get("guild", 0)) == int(victim_e.get("guild", 0)): return false # if (victim.IsKiller()) if (!IsSamePartyMember(...)) return true; if _is_killer(victim_e): if not _is_same_party(sv, vv, ctx): return true # if (PK_MODE_PROTECT != GetPKMode()) { ... } if self_pk != PK_MODE_PROTECT: if self_pk == PK_MODE_FREE: if _pk_mode_of(victim_e) != PK_MODE_PROTECT: if not _is_same_party(sv, vv, ctx): return true if self_pk == PK_MODE_GUILD: if _pk_mode_of(victim_e) != PK_MODE_PROTECT: if not _is_same_party(sv, vv, ctx): if int(self_e.get("guild", 0)) != int(victim_e.get("guild", 0)): return true # if (IsSameEmpire(victim)) { ... } else { return true; } if _is_same_empire(self_e, victim_e): if _is_pvp_instance(self_e, victim_e, ctx): return true if self_pk == PK_MODE_REVENGE: if not _is_same_party(sv, vv, ctx): if is_conflict_alignment_instance(self_e, victim_e): return true else: return true if _is_enemy(victim_e): return true if _is_wooden_door(victim_e): return true elif _is_enemy(self_e): if _is_pc(victim_e): return true if _is_building(victim_e): return true elif _is_poly(self_e): if _is_pc(victim_e): return true if _is_enemy(victim_e): return true return false