Files
mtgodot-poc/project/guild_mark_tail_test.gd

115 lines
4.5 KiB
GDScript

# guild_mark_tail_test —— 已下载的公会会徽接入角色头顶尾标。
# 覆盖会徽晚于角色实体到达、参考端 mark/guild/name 的水平排布,以及清空会徽缓存后
# 不伪留旧 Sprite3D。godot --headless --path project --script guild_mark_tail_test.gd
extends SceneTree
const NetWorld = preload("res://net_world.gd")
const NameTailLayout = preload("res://name_tail_layout.gd")
class FakeClient extends Node:
signal entity_spawned(entity: Dictionary)
signal entity_despawned(vid: int)
signal entity_moved(vid: int)
signal entity_main_set(vid: int)
signal entity_info(vid: int, entity: Dictionary)
signal chat(type: int, vid: int, text: String)
signal vitals_changed(vid: int)
signal entity_dead(vid: int)
signal damage(vid: int, amount: int, flag: int)
signal guild_marks_ready(mark_count: int)
signal guild_mark_updated(guild_id: int, img_idx: int)
var entities := {}
var mark: Image
func get_entity(vid: int) -> Dictionary: return entities.get(vid, {})
func get_entities() -> Array: return entities.values()
func get_pvp_relations() -> Array: return []
func get_duel() -> Dictionary: return {}
func get_guild_name(guild_id: int) -> String: return "Wolves" if guild_id == 88 else ""
func get_guild_mark_image(guild_id: int) -> Image:
return mark if guild_id == 88 else null
func add_entity(vid: int, guild_id: int) -> Dictionary:
var d := {"vid": vid, "name": "Alice", "guild": guild_id, "level": 42,
"alignment": 0, "ch_type": 0, "race": 0, "hp": 100, "max_hp": 100,
"dead": false, "pos": Vector3.ZERO, "pos_cm": Vector3.ZERO,
"func": 0, "moving": false, "angle_deg": 0.0, "parts": [0, 0, 0, 0],
"affect_flags": 0}
entities[vid] = d
return d
var _fail := 0
func _ck(condition: bool, message: String) -> void:
if not condition:
_fail += 1
printerr("FAIL: " + message)
func _mark_image(color: Color) -> Image:
var bytes := PackedByteArray()
bytes.resize(16 * 12 * 4)
for i in range(0, bytes.size(), 4):
bytes[i] = int(color.r * 255.0)
bytes[i + 1] = int(color.g * 255.0)
bytes[i + 2] = int(color.b * 255.0)
bytes[i + 3] = 255
return Image.create_from_data(16, 12, false, Image.FORMAT_RGBA8, bytes)
func _init() -> void:
await _run()
if _fail == 0:
print("PASS: guild_mark_tail_test")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
var mount := Node3D.new()
get_root().add_child(mount)
var client := FakeClient.new()
get_root().add_child(client)
var world: Node = NetWorld.new()
get_root().add_child(world)
world.setup(client, mount)
# The character is rendered before the side connection finishes downloading marks.
var entity := client.add_entity(7, 88)
client.entity_spawned.emit(entity)
var actor: Node3D = world.node_for(7)
_ck(actor != null, "guild actor is spawned")
_ck(actor.get_node_or_null("GuildMark") == null, "no mark image -> no stale GuildMark")
# Mark download completion refreshes already-visible actors.
client.mark = _mark_image(Color(0.2, 0.6, 0.9))
client.guild_marks_ready.emit(1)
var mark := actor.get_node_or_null("GuildMark") as Sprite3D
var guild := actor.get_node_or_null("GuildTag") as Label3D
_ck(mark != null and mark.texture != null, "guild_marks_ready -> world GuildMark Sprite3D")
_ck(mark != null and mark.texture.get_width() == 16 and mark.texture.get_height() == 12,
"GuildMark keeps the 16x12 mark dimensions")
_ck(guild != null and guild.text == "Wolves", "guild tag remains visible beside the mark")
_ck(mark != null and guild != null and not is_zero_approx(guild.position.x),
"mark present -> guild name gets mark-relative x offset")
_ck(mark != null and is_equal_approx(mark.position.y,
world.NAME_TAG_DEFAULT_Y + NameTailLayout.MARK_ABOVE_NAME_PX * actor.get_node("Label3D").pixel_size),
"mark is placed 26px above the name row")
# An incremental update replaces the texture in place rather than duplicating the node.
var old_mark := mark
client.mark = _mark_image(Color(0.9, 0.2, 0.2))
client.guild_mark_updated.emit(88, 3)
var refreshed := actor.get_node_or_null("GuildMark") as Sprite3D
_ck(refreshed == old_mark, "guild_mark_updated reuses the existing Sprite3D")
_ck(refreshed.texture != null and refreshed.texture.get_image().get_pixel(0, 0).r > 0.8,
"guild_mark_updated refreshes the texture")
# GC_CHAR_ADD_INFO can remove a guild; the mark is removed with the guild tag.
entity["guild"] = 0
client.entity_info.emit(7, entity)
await process_frame
_ck(actor.get_node_or_null("GuildMark") == null, "guild removal deletes the mark instance")
world.free()
mount.free()
client.free()