Files
mtgodot-poc/project/entity_rules.gd
T

262 lines
9.6 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.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 IsWoodenDoorvnum 13000 / 30111..30119
#
# 纯静态谓词。调用方(net_play.gd)负责把网络快照拼成 self_e / victim_e / ctx
# 部分依赖在本 40250 客户端的发布源码里没有 setter(同 §3.4/§3.6/§3.8 的处理),
# 在下方逐条标注为 seam。
extends RefCounted
# EPKModesREF/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 typeREF/UserInterface/InstanceBase.h:249
const ALIGNMENT_TYPE_WHITE := 0
const ALIGNMENT_TYPE_NORMAL := 1
const ALIGNMENT_TYPE_DARK := 2
# duel modeREF/UserInterface/InstanceBase.h:378GetDuelMode 返回 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 / partyInstanceBase.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::GetAlignmentGradeInstanceBase.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::GetAlignmentTypeInstanceBase.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::IsWoodenDoorInstanceBase.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::IsSameEmpireInstanceBase.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::__FindDUELKeyInstanceBaseEffect.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::IsPVPInstanceInstanceBaseEffect.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::IsConflictAlignmentInstanceInstanceBase.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::IsAttackableInstanceInstanceBase.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