完善客户端功能并同步差距文档
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
# 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
|
||||
const KIND_PC := 0
|
||||
const KIND_NPC := 1
|
||||
const KIND_MONSTER := 2
|
||||
const KIND_STONE := 3
|
||||
const KIND_WARP := 4
|
||||
|
||||
# --- 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)
|
||||
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()。
|
||||
# 建筑 / 城墙的 thing type 在本 POC 未单独下发(IsWall 的 race 白名单未随源码发布)→ seam。
|
||||
return bool(e.get("building", false))
|
||||
|
||||
# --- 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
|
||||
Reference in New Issue
Block a user