# text_tail_arrange —— §8.8 seam ⑩ 收口:地面物品名条「投影后互相挤开防重叠」1:1。 # CPythonTextTail::ArrangeTextTail 的 m_ItemTextTailList 块(PythonTextTail.cpp:160-190): # 逐条 item 尾标,与其它每条比 isIn(AABB 相交),命中就把自己顶到对方框底下 # `pCompareTextTail->y + pCompareTextTail->yEnd + yTemp`(yTemp = 5),内层循环从头重跑, # LimitCount 上限 20 次防死循环。isIn(:489-507)是标准 AABB:两框在 x、y 都有交叠即真。 # 尾标框(:810-813 等):xStart = -w/2-2、xEnd = w/2+2、yStart = -2、yEnd = h。 # (与 name_color / text_tail / chat_tail / name_show / damage_effect / target_board / # pick_show / name_tail_layout 同一套纯静态镜像套路,本模块是第 29 个 canonical。) # # REF(40250 ClientVS22 source/ 为唯一基线): # UserInterface/PythonTextTail.cpp:19 const float c_TextTail_Name_Position = -10.0f # UserInterface/PythonTextTail.cpp:153 ArrangeTextTail() # :160 for (pInsertTextTail : m_ItemTextTailList) —— 逐条 item 名条 # :164 int yTemp = 5; int LimitCount = 0; # :167 for (pCompareTextTail : m_ItemTextTailList) —— 内层比对 # :171 if (*itorCompare == *itor) { ++itorCompare; continue; } —— 跳过自己 # :176 if (LimitCount >= 20) break; # :180 if (isIn(pInsertTextTail, pCompareTextTail)) # :182 pInsertTextTail->y = pCompareTextTail->y + pCompareTextTail->yEnd + yTemp; # :184 itorCompare = m_ItemTextTailList.begin(); ++LimitCount; continue; —— 从头重跑 # :189 ++itorCompare; # UserInterface/PythonTextTail.cpp:489 isIn(pSource, pTarget): # 框 Source = [x+xStart, x+xEnd] × [y+yStart, y+yEnd],Target 同; # :500 if (x1S <= x2T && x2S >= x1T && y1S <= y2T && y2S >= y1T) return true; # UserInterface/PythonTextTail.cpp:810-813 尾标框(SetTextTailOwnerText 等): # xStart = -xSize/2 - 2、yStart = -2、xEnd = xSize/2 + 2、yEnd = ySize(xSize/ySize = 文本像素尺寸) # UserInterface/PythonTextTail.cpp:706-737 SetItemTextTailOwner(dwVID, c_szName):掉落名条 # 挂 owner 后缀。owner 非空时 owner 走独立 CGraphicTextInstance(黄 1,1,0 / CENTER / 文本 # = c_szName + strOwnership,IDS_POSSESSIVE_MORPHENE 为空取字面 "'s"),单独 GetTextSize: # :735 pTextTail->yEnd += float(yOwnerSize + 4) —— owner 另起一行、行距 4 # :736 pTextTail->xStart = fMIN(xStart, -xOwnerSize/2 - 1) # :737 pTextTail->xEnd = fMAX(xEnd, xOwnerSize/2 + 1) # owner 为空时删 owner 实例、框复位成纯名字 xStart=-w/2-2 / xEnd=w/2+2 / yEnd=h。 # # seam ⑩:参考端在屏幕像素空间跑(y 向下),poc 地面物品名条是每物一个 billboard Label3D。 # 本模块保持屏幕像素语义(ground_items 把 tag 世界锚点投影到屏幕、按文本长度估框),算出 # 每条被下推的屏幕 y 后,poc 侧按 name_tail_layout 的同一 pixel_size 约定折成世界 y 向下的 # 位移(屏幕向下 Δpx → 世界向下 Δpx*pixel_size)。文本框尺寸按字符数估(参考端是 # GetTextSize 精确像素)= seam ⑩ 近似;只对 item 名条做,角色名字尾标不参与(同参考)。 extends RefCounted const PUSH_GAP := 5.0 # ArrangeTextTail :164 的 yTemp const MAX_ITERS := 20 # :176 的 LimitCount 上限(单条最多被下推的次数) # 尾标框相对锚点 (x, y) 的四边(PythonTextTail.cpp:810-813)。屏幕坐标,y 向下。 static func box_start_x(w: float) -> float: return -w / 2.0 - 2.0 static func box_end_x(w: float) -> float: return w / 2.0 + 2.0 static func box_start_y() -> float: return -2.0 static func box_end_y(h: float) -> float: return h # SetItemTextTailOwner(PythonTextTail.cpp:706-737)代入 poc 的 {w, h} 框:掉落名条挂上 # 「owner + 所有格语素」后,去重叠框在纯名字框(name_w / name_h)基础上按 owner 独立 # 文本度量(owner_w / owner_h)扩张: # :735 yEnd += ownerH + 4 -> h = name_h + owner_h + 4 # :737 xEnd = fMAX(nameW/2+2, ownerW/2+1) # box_end_x(w) = w/2 + 2,令它等于 fMAX 结果 -> w = max(name_w, owner_w - 2) # (框 x 两边对称,box_start_x 自动 = 参考 :736 的 fMIN 结果,无需单独算)。 # owner_w / owner_h <= 0(无 owner,参考端删 owner 实例、框复位纯名字)-> {name_w, name_h}。 static func item_box_with_owner(name_w: float, name_h: float, owner_w := 0.0, owner_h := 0.0) -> Dictionary: if owner_w <= 0.0 or owner_h <= 0.0: return {"w": name_w, "h": name_h} return {"w": maxf(name_w, owner_w - 2.0), "h": name_h + owner_h + 4.0} # isIn(PythonTextTail.cpp:489-507):两条尾标框在屏幕上是否相交(含边界,<= / >=)。 # a / b 为 {x, y, w, h}(x, y = 锚点屏幕坐标;w, h = 文本像素尺寸)。 static func boxes_overlap(a: Dictionary, b: Dictionary) -> bool: var ax1: float = a["x"] + box_start_x(a["w"]) var ay1: float = a["y"] + box_start_y() var ax2: float = a["x"] + box_end_x(a["w"]) var ay2: float = a["y"] + box_end_y(a["h"]) var bx1: float = b["x"] + box_start_x(b["w"]) var by1: float = b["y"] + box_start_y() var bx2: float = b["x"] + box_end_x(b["w"]) var by2: float = b["y"] + box_end_y(b["h"]) return ax1 <= bx2 and ax2 >= bx1 and ay1 <= by2 and ay2 >= by1 # ArrangeTextTail :160-190 的 item 块。tails: Array[{x, y, w, h}](屏幕坐标,y 向下), # 列表顺序即参考端 m_ItemTextTailList 顺序。返回等长 Array[float]:每条排布后的屏幕 y # (未重叠的保持原值)。逐条 pInsert 与其它每条 pCompare 比 isIn,命中就把 pInsert 顶到 # (pCompare.y + pCompare.yEnd + PUSH_GAP),内层从头重跑,单条最多下推 MAX_ITERS 次。 # pCompare 用的是「已排布后」的 y(与参考端共用一张链表、y 就地改写一致)。 static func arrange_item_tails(tails: Array) -> Array: var ys: Array = [] for t in tails: ys.append(float(t["y"])) var n := tails.size() for i in n: var limit := 0 var j := 0 while j < n: if j == i: j += 1 continue if limit >= MAX_ITERS: break var a := {"x": tails[i]["x"], "y": ys[i], "w": tails[i]["w"], "h": tails[i]["h"]} var b := {"x": tails[j]["x"], "y": ys[j], "w": tails[j]["w"], "h": tails[j]["h"]} if boxes_overlap(a, b): ys[i] = ys[j] + box_end_y(tails[j]["h"]) + PUSH_GAP j = 0 limit += 1 continue j += 1 return ys