115 lines
4.5 KiB
GDScript
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()
|