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
+150
View File
@@ -0,0 +1,150 @@
# name_tail_layout —— §8.8 seam ⑨ 收口:头顶名字尾标「有聊天气泡时上移贴气泡」1:1。
# CPythonTextTail::UpdateShowingTextTail 里,每条角色名字尾标投影完屏幕坐标后,
# 若该 VID 同时有一条 chat 尾标且 `pChatTail->bNameFlag`(= RegisterChatTail 注册的
# 「聊天」气泡,非 RegisterInfoTail 的「信息」气泡),就把名字尾标的屏幕 y 顶到
# `chatTail.y - 17.0f`(屏幕 y 向下,减 17px = 上移一行,正好贴在气泡上沿)。
# (与 name_color.gd / text_tail.gd / chat_tail.gd / name_show.gd / damage_effect.gd /
# target_board.gd / pick_show.gd 同一套纯静态镜像套路,本模块是第 28 个 canonical。)
#
# REF40250 ClientVS22 source/ 为唯一基线):
# UserInterface/PythonTextTail.cpp:19 const float c_TextTail_Name_Position = -10.0f
# UserInterface/PythonTextTail.cpp:509 RegisterCharacterTextTail(..., float fAddHeight=10.0f)
# :519 fHeight = GetHeight() + fAddHeight —— 名字尾标世界高=角色高 + 10
# UserInterface/PythonTextTail.cpp:581 RegisterChatTail:新建时 fHeight = GetHeight() + 10.0f、
# :603 / :618 pTextTail->bNameFlag = TRUE —— 「聊天」气泡(白)
# UserInterface/PythonTextTail.cpp:623 RegisterInfoTail:同高,
# :646 / :661 pTextTail->bNameFlag = FALSE —— 「信息」气泡(淡红),不顶名字
# UserInterface/PythonTextTail.cpp:88 UpdateShowingTextTail()
# :101 for (角色名字尾标 : m_CharacterTextTailList)
# :104 UpdateTextTail(pTextTail) —— 先投影出屏幕 x/y/z
# :106 itor = m_ChatTailMap.find(pTextTail->dwVirtualID)
# :110 if (m_ChatTailMap.end() != itor)
# :112 if (pChatTail->bNameFlag)
# :114 pTextTail->y = pChatTail->y - 17.0f —— 名字屏幕 y 顶到气泡上方 17px
# UserInterface/PythonTextTail.cpp:74 UpdateAllTextTail:同一个 bNameFlag 还驱动
# ShowCharacterTextTail(dwVID) 强显(增量 112/113 已接 chat_tail.gd / name_show.gd)。
# UserInterface/PythonTextTail.cpp:151 ArrangeTextTail()
# :211 for (pTextTail : m_CharacterTextTailList) —— 逐条角色名字尾标
# :228 pGuildNameInstance->SetPosition(x + iImageHalfSize, pTextTail->y - c_fyGuildNamePosition, z)
# :21 const float c_fyGuildNamePosition = 15.0f —— 公会名锚在名字 y 上方 15px
# :258 pTitle->SetPosition(x - iNameWidth/2 - fxAdd, pTextTail->y, z) —— 称号与名字同一行
# :283 pLevel->SetPosition(x - iNameWidth/2 - fxAdd - iTitleWidth, pTextTail->y, z) —— 等级同一行
# —— 公会名 / 称号 / 等级三个子标签的屏幕 y 全部取自 `pTextTail->y`,而这个 y 已在
# UpdateShowingTextTail :114 被聊天气泡顶过 -17。所以名字尾标一旦上移,子标签整体跟随。
#
# seam ⑨(已收口):参考端名字与气泡默认投影到同一世界点(GetHeight()+10),靠这条屏幕
# -17px 把名字从「和气泡重叠」顶成「贴气泡上沿」。poc 是每实体一个 billboard Label3D、
# 气泡默认在名字上方(局部 y 2.35 vs 2.1),本模块把屏幕 y 向下的 -17px 换成世界 y
# 向上的 +17px*pixel_size:有「聊天」气泡时名字世界 y 顶到气泡 y 之上 17px,气泡消失
# (或只是「信息」气泡)时回默认 y。像素 vs 世界的换算按名字 Label3D 的 pixel_size
# 与参考的字体无关常量 17 对齐。
# seam ⑦(部分收口 · 增量 119):子标签(GuildTag/TitleTag/LevelTag)随名字尾标纵向位移
# 已 1:1name_tail_lift_delta / subtag_world_y:名字上移多少子标签整体上移多少)。
# seam ⑦(水平排版 · 增量 121):ArrangeTextTail 角色块(PythonTextTail.cpp:211-320)非
# EUROPE 分支的逐像素水平排布也已 1:1(下方 name_x_shift / title_x_offset /
# level_x_offset / guild_name_x_offset / mark_x_offset / subtag_world_x):
# :236 fxAdd = 0 —— 无称号无等级
# :242 if (pTitle) fxAdd = 8.0f
# :258 pTitle->SetPosition(x - iNameWidth/2 - fxAdd, y, z) (称号,居中对齐)
# :263 if (pLevel) —— 有称号时等级在称号左侧
# :282 pLevel->SetPosition(x - iNameWidth/2 - fxAdd - iTitleWidth, y, z)(等级,右对齐)
# :288 else fxAdd = 4.0f —— 只有等级
# :312 pLevel->SetPosition(x - iNameWidth/2 - fxAdd, y, z)
# :320 pTextInstance->SetPosition(x + fxAdd, y, z) —— 名字本身右移 fxAdd
# :224 iImageHalfSize = pMarkInstance->GetWidth()/2 + c_fxMarkPosition(1.5)
# :227 pMarkInstance->SetPosition(x - guildW/2 - iImageHalfSize, y - c_fyMarkPosition(26))
# :228 pGuildNameInstance->SetPosition(x + iImageHalfSize, y - c_fyGuildNamePosition(15), z)
# —— 公会名的水平锚点只在「有公会徽记 pMarkInstance」时才被 ArrangeTextTail 改写。
# 仍近似(seam ⑦ 遗留):① poc 无公会徽记 mark 实体 —— 参考端 :220 的
# `if (pMarkInstance && pGuildNameInstance)` 不成立时公会名水平锚点不动,poc 亦让
# GuildTag 留在名字中线正上方(x 偏移 0),不做 +markW/2+1.5;② 文本宽度 poc 用
# 「字符数 * 约 7px」估,参考端是 GetTextSize 精确像素;③ 三子标签竖排 vs 参考同一
# 水平行(poc 3D billboard 里三条挤在一行会互相糊,保留竖排换 x 偏移的折衷)。
extends RefCounted
# PythonTextTail.cpp:114 的 `17.0f`。屏幕坐标下是「名字 y 减 17」(上移一行贴气泡)。
const NAME_OFFSET_ABOVE_BUBBLE_PX := 17.0
# PythonTextTail.cpp:21 的 `c_fyGuildNamePosition = 15.0f`:参考端公会名锚在名字尾标
# 屏幕 y 上方 15px(称号 / 等级则与名字同一行)。poc 竖排近似不用它做像素排版,仅作
# 1:1 记录——子标签真正跟随的是「名字尾标位移量」,见 subtag_world_y。
const GUILD_NAME_ABOVE_NAME_PX := 15.0
# UpdateShowingTextTail :110 `m_ChatTailMap.end() != itor` && :112 `pChatTail->bNameFlag`。
# has_chat_tail —— 该 VID 在 m_ChatTailMap 里有一条(聊天或信息);
# chat_name_flag —— 那条的 bNameFlagRegisterChatTail=TRUE / RegisterInfoTail=FALSE)。
static func name_follows_bubble(has_chat_tail: bool, chat_name_flag: bool) -> bool:
return has_chat_tail and chat_name_flag
# 参考端字面公式(屏幕坐标,y 向下):follows 时 `pChatTail->y - 17.0f`,否则名字投影原值。
static func name_tail_screen_y(default_y: float, bubble_y: float, follows: bool) -> float:
if follows:
return bubble_y - NAME_OFFSET_ABOVE_BUBBLE_PX
return default_y
# poc 3D 适配(世界坐标,y 向上):屏幕的 -17px 变成 +17px*pixel_size —— follows 时名字
# 世界 y 顶到气泡世界 y 之上 17 个名字像素,否则回默认世界 y。
static func name_tail_world_y(default_world_y: float, bubble_world_y: float,
follows: bool, px_size: float) -> float:
if follows:
return bubble_world_y + NAME_OFFSET_ABOVE_BUBBLE_PX * px_size
return default_world_y
# 名字尾标相对默认世界 y 的位移量(follows 时 = 顶到气泡上方后的净上移,否则 0)。
# ArrangeTextTail 里公会名 / 称号 / 等级都锚在 pTextTail->y 上,位移量就是子标签要跟随的量。
static func name_tail_lift_delta(default_world_y: float, bubble_world_y: float,
follows: bool, px_size: float) -> float:
return name_tail_world_y(default_world_y, bubble_world_y, follows, px_size) - default_world_y
# ArrangeTextTail :228 / :258 / :283:子标签屏幕 y 取自 pTextTail->y(已被聊天气泡顶过)。
# poc 里子标签各有自己的默认局部 y(竖排近似),名字尾标上移 lift_delta 时三者整体加同一段。
static func subtag_world_y(default_subtag_y: float, name_tail_lift_delta: float) -> float:
return default_subtag_y + name_tail_lift_delta
# --- seam ⑦ 水平排版(ArrangeTextTail 角色块,PythonTextTail.cpp:211-320 非 EUROPE 分支)---
# :242 `fxAdd = 8.0f`if pTitle)。屏幕像素,名字锚点 x 与三子标签公用。
const NAME_X_ADD_TITLE := 8.0
# :288 `fxAdd = 4.0f`else,只有等级)。
const NAME_X_ADD_LEVEL_ONLY := 4.0
# :20 `const float c_fxMarkPosition = 1.5f`。
const MARK_X_PAD := 1.5
# :22 `const float c_fyMarkPosition = 15.0f + 11.0f`。公会徽记锚在名字 y 上方 26px。
const MARK_ABOVE_NAME_PX := 26.0
# :320 `pTextInstance->SetPosition(pTextTail->x + fxAdd, ...)`:名字本身相对锚点右移 fxAdd。
# 有称号 -> 8;无称号只有等级 -> 4;都没有 -> 0。
static func name_x_shift(has_title: bool, has_level: bool) -> float:
if has_title:
return NAME_X_ADD_TITLE
if has_level:
return NAME_X_ADD_LEVEL_ONLY
return 0.0
# :258 `pTitle->SetPosition(pTextTail->x - (iNameWidth/2) - fxAdd, ...)`fxAdd = 8)。
# 返回称号中心相对名字锚点 x 的像素偏移(负 = 在名字左侧)。
static func title_x_offset(name_w: float) -> float:
return -(name_w / 2.0) - NAME_X_ADD_TITLE
# 等级相对名字锚点 x 的像素偏移(右对齐实例,故这是其右边缘位置):
# 有称号(:282fxAdd = 8):x - iNameWidth/2 - 8 - iTitleWidth —— 再往称号左边挪一个称号宽;
# 只有等级(:312fxAdd = 4):x - iNameWidth/2 - 4。
static func level_x_offset(name_w: float, title_w: float, has_title: bool) -> float:
if has_title:
return -(name_w / 2.0) - NAME_X_ADD_TITLE - title_w
return -(name_w / 2.0) - NAME_X_ADD_LEVEL_ONLY
# :224/:228 公会名中心 = x + iImageHalfSizeiImageHalfSize = markW/2 + c_fxMarkPosition(1.5)。
# 仅在有公会徽记时 ArrangeTextTail 才改写公会名 x;无徽记(poc 现状)应传 mark_w = 0
# 并按「参考端此路不改写」处理(调用方直接留 0 偏移),此函数仅供有徽记时用。
static func guild_name_x_offset(mark_w: float) -> float:
return mark_w / 2.0 + MARK_X_PAD
# :227 徽记位置 = x - guildW/2 - iImageHalfSize(相对名字锚点 x 的像素偏移,负 = 左侧)。
static func mark_x_offset(guild_w: float, mark_w: float) -> float:
return -(guild_w / 2.0) - (mark_w / 2.0 + MARK_X_PAD)
# poc 3D 适配:参考端在屏幕像素空间摆位(+x 向右),poc 子标签是 billboard Label3D ——
# billboard 本地 +x 恒等于屏幕右,按该 Label3D 的 pixel_size 把像素偏移折成世界本地 x。
static func subtag_world_x(px_offset: float, px_size: float) -> float:
return px_offset * px_size