Files
mtgodot-poc/project/text_tail_arrange.gd

106 lines
6.5 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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。)
#
# REF40250 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 = ySizexSize/ySize = 文本像素尺寸)
# UserInterface/PythonTextTail.cpp:706-737 SetItemTextTailOwner(dwVID, c_szName):掉落名条
# 挂 owner 后缀。owner 非空时 owner 走独立 CGraphicTextInstance(黄 1,1,0 / CENTER / 文本
# = c_szName + strOwnershipIDS_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
# SetItemTextTailOwnerPythonTextTail.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}
# isInPythonTextTail.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