# 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。) # # REF(40250 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 # SetHPTargetBoard:vid != GetTargetVID() → 先 ResetTargetBoard()+SetEnemyVID(vid)。 static func needs_reset(new_vid: int, cur_vid: int) -> bool: return new_vid != cur_vid # CloseTargetBoardIfDifferent:vid != 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)