Complete remaining client gap features

This commit is contained in:
shenlei
2026-09-04 13:25:05 +09:00
parent 13ccb8d02d
commit 9a4a044da5
47 changed files with 6667 additions and 140 deletions
+91
View File
@@ -0,0 +1,91 @@
# target_board —— §5.4 / §8.8 目标框(TargetBoard)收包分派 1:1
# CPythonNetworkStream::RecvTargetPacket 的「目标存在 / 未死 → PC|Building 关掉异 VID 框、
# 可看血就设血条、否则关框」判定树,加 CInstanceBase::CanViewTargetHP 的
# IsStone()||IsWoodenDoor()||IsEnemy(),再加 game.py 的 SetHPTargetBoard(换 VID 先 Reset/
# CloseTargetBoardIfDifferent / CloseTargetBoard / SetPCTargetBoard 的 LCONTROL 悄悄话捷径。
# (与 name_color.gd / text_tail.gd / chat_tail.gd / name_show.gd / damage_effect.gd 同一套
# 纯静态镜像套路,本模块是第 26 个 canonical。)
#
# REF40250 ClientVS22 source/ 为唯一基线):
# UserInterface/Packet.h:1937 TPacketGCTarget { BYTE header; DWORD dwVID; BYTE bHPPercent; }
# UserInterface/PythonNetworkStreamPhaseGame.cpp:2424 RecvTargetPacket()
# :2436 if (pInstPlayer && pInstTarget) —— 主角与目标实例都在
# :2438 if (!pInstTarget->IsDead()) —— 目标没死透才动框
# :2440 if (IsPC() || IsBuilding()) CloseTargetBoardIfDifferent(dwVID)
# :2442 else if (CanViewTargetHP(*pInstTarget)) SetHPTargetBoard(dwVID, bHPPercent)
# :2444 else CloseTargetBoard()
# :2447 m_pInstTarget = pInstTarget —— 仅当目标存在且未死
# :2453 else CloseTargetBoard() —— 实例缺失
# UserInterface/InstanceBase.cpp:2293 CanViewTargetHP(victim) = victim.IsStone()||victim.IsWoodenDoor()||victim.IsEnemy()
# UserInterface/InstanceBase.cpp:2380/2390 IsBuilding() / IsWoodenDoor()vnum 13000 或 30111..30119
# root/game.py:804 SetPCTargetBoard(vid,name)Open(vid,name);若按住 LCONTROL 且 IsSameEmpire
# 且非本人且非 building → interface.OpenWhisperDialog(name)
# root/game.py:829 SetHPTargetBoard(vid,hpPct)vid != GetTargetVID() → ResetTargetBoard()+SetEnemyVID(vid)SetHP(hpPct)+Show()
# root/game.py:837 CloseTargetBoardIfDifferent(vid)vid != GetTargetVID() → Close()
# root/game.py:841 CloseTargetBoard()Close()
# root/uitarget.py:189-193 TargetBoard.Open(vid,name) 最前置过滤:
# if not constInfo.GET_VIEW_OTHER_EMPIRE_PLAYER_TARGET_BOARD():
# if not player.IsSameEmpire(vid): self.Hide(); return
# —— game-option「看他国玩家目标框」关掉时,异阵营玩家框直接不开(constinfo 默认 1 = 开)。
#
# seam ⑫:旧实体快照没有独立建筑字段;当前 net_play 已支持 fixture / mob_proto
# 的 BUILDING(5) / WOODEN_DOOR(6) 分类。没有分类信息的旧快照仍按 NPC/怪/石头路径。
# 另:poc 的 target_info 由 C++ 侧(extension/src/net/m2_client.cpp)从 EntityStore 抽出,
# 本模块只镜像 GDScript 侧对 (vid, hp%) 的分派,不改 C++ 收包。
# LCONTROL 悄悄话捷径:增量 122 接线 —— net_play._on_pick 在 KIND_PC 分支轮询
# whisper_on_ctrl_click(_lcontrol_down, same_empire, vid==_main_vid, false),真则发
# whisper_requested(name)game_scene 转 chat.start_whisper()poc 版 OpenWhisperDialog);
# _lcontrol_down 由 game_scene 的 KEY_CTRL 按下 / 松开喂。
extends RefCounted
# RecvTargetPacket 分派结果。
# CLOSE —— 关框(实例缺失 / 不可看血的非 PC 非 building 目标)
# CLOSE_IF_DIFFERENT —— 目标是 PC 或 building:仅当框上是别的 VID 才关
# SET_HP —— 可看血:设血条并显示(换 VID 先 Reset)
# NONE —— 目标已死透:不动框,但仍更新 m_pInstTarget
enum Action { CLOSE, CLOSE_IF_DIFFERENT, SET_HP, NONE }
# CanViewTargetHP(victim):石头 / 木门 / 敌对,任一即可看血。
static func can_view_target_hp(is_stone: bool, is_wooden_door: bool, is_enemy: bool) -> bool:
return is_stone or is_wooden_door or is_enemy
# RecvTargetPacket 判定树。target_exists = (pInstPlayer && pInstTarget)。
static func classify(target_exists: bool, is_dead: bool, is_pc: bool,
is_building: bool, can_view_hp: bool) -> int:
if not target_exists:
return Action.CLOSE # :2453 else CloseTargetBoard()
if is_dead:
return Action.NONE # :2438 !IsDead() 为假 —— 整块跳过
if is_pc or is_building:
return Action.CLOSE_IF_DIFFERENT # :2440
if can_view_hp:
return Action.SET_HP # :2442
return Action.CLOSE # :2444
# m_pInstTarget = pInstTarget 只在「目标存在且没死透」时写(:2447 在 !IsDead() 块内)。
static func updates_current_target(target_exists: bool, is_dead: bool) -> bool:
return target_exists and not is_dead
# SetHPTargetBoardvid != GetTargetVID() → 先 ResetTargetBoard()+SetEnemyVID(vid)。
static func needs_reset(new_vid: int, cur_vid: int) -> bool:
return new_vid != cur_vid
# CloseTargetBoardIfDifferentvid != GetTargetVID() 才 Close()。
static func should_close_if_different(vid: int, cur_vid: int) -> bool:
return vid != cur_vid
# TargetBoard.Open :189-193 前置过滤:game-option「看他国玩家目标框」(constinfo
# VIEW_OTHER_EMPIRE_PLAYER_TARGET_BOARD,默认 1) 关掉时,异阵营目标框不开。
# view_other_empire=false 且 same_empire=false → 隐藏。empire 0 的怪/NPC/石头在
# IsSameEmpire 里算「同阵营」(entity_rules._is_same_empire),故只挡真·他国 PC。
static func hidden_as_other_empire(view_other_empire: bool, same_empire: bool) -> bool:
return not view_other_empire and not same_empire
# SetPCTargetBoard 的 LCONTROL 悄悄话捷径:按住 LCONTROL 且同阵营且非本人且非 building。
static func whisper_on_ctrl_click(lcontrol_down: bool, same_empire: bool,
is_main: bool, is_building: bool) -> bool:
return lcontrol_down and same_empire and not is_main and not is_building
# bHPPercent 是 BYTE,钳到 0..100。
static func clamp_hp_pct(v: Variant) -> int:
return clampi(int(v), 0, 100)