106 lines
6.5 KiB
GDScript
106 lines
6.5 KiB
GDScript
# 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
|