99 lines
3.5 KiB
GDScript
99 lines
3.5 KiB
GDScript
# screen_hp_bar_test —— §8.8 屏幕空间 HP 条 headless 自检。
|
|
# godot --headless --path project --script screen_hp_bar_test.gd
|
|
# 覆盖:不再创建 3D Sprite3D、实体血量同步、世界坐标投影、镜头后/死亡隐藏、地图清理。
|
|
extends SceneTree
|
|
|
|
const NetWorld = preload("res://net_world.gd")
|
|
const ScreenHpBar = preload("res://screen_hp_bar.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)
|
|
var entities := {}
|
|
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 add(vid: int, pos: Vector3, main := false) -> Dictionary:
|
|
var d := {"vid": vid, "name": "e%d" % vid, "is_main": main,
|
|
"pos": pos, "pos_cm": Vector3(pos.x * 100.0, -pos.z * 100.0, 0.0),
|
|
"func": 0, "moving": false, "angle_deg": 0.0,
|
|
"hp": 100, "max_hp": 100, "dead": false, "race": 0,
|
|
"affect_flags": 0, "parts": [0, 0, 0, 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 _init() -> void:
|
|
await _run()
|
|
if _fail == 0:
|
|
print("PASS: screen_hp_bar_test (§8.8 screen-space HP bar)")
|
|
quit(0)
|
|
else:
|
|
printerr("%d check(s) failed" % _fail)
|
|
quit(1)
|
|
|
|
func _run() -> void:
|
|
var mount := Node3D.new()
|
|
var client := FakeClient.new()
|
|
var camera := Camera3D.new()
|
|
var nw: Node = NetWorld.new()
|
|
get_root().add_child(mount)
|
|
get_root().add_child(client)
|
|
get_root().add_child(camera)
|
|
get_root().add_child(nw)
|
|
camera.position = Vector3(0.0, 2.0, 6.0)
|
|
camera.look_at_from_position(Vector3(0.0, 2.0, 6.0), Vector3.ZERO, Vector3.UP)
|
|
camera.current = true
|
|
nw.setup(client, mount)
|
|
nw.set_camera(camera)
|
|
client.add(1, Vector3.ZERO, true)
|
|
client.entity_main_set.emit(1)
|
|
nw.set_local_vid(1)
|
|
var entity := client.add(2, Vector3.ZERO)
|
|
client.entity_spawned.emit(entity)
|
|
await process_frame
|
|
await process_frame
|
|
|
|
var node: Node3D = nw.node_for(2)
|
|
var bar := nw._hp_bars.get(2, null) as Control
|
|
_ck(node != null, "remote entity exists")
|
|
_ck(bar != null and bar is ScreenHpBar, "entity owns a screen Control HP bar")
|
|
_ck(node.get_node_or_null("HpBar") == null, "legacy Sprite3D HpBar removed")
|
|
_ck(bar != null and bar.visible, "front-facing living entity shows HP bar")
|
|
if bar != null:
|
|
var expected := camera.unproject_position(node.global_position + Vector3(0, nw.HP_BAR_WORLD_Y, 0))
|
|
_ck(is_equal_approx(bar.position.x, expected.x - bar.size.x * 0.5),
|
|
"bar x is centered on projected world anchor")
|
|
_ck(is_equal_approx(bar.ratio, 1.0), "initial hp ratio = 1")
|
|
|
|
entity["hp"] = 25
|
|
client.vitals_changed.emit(2)
|
|
await process_frame
|
|
_ck(is_equal_approx(bar.ratio, 0.25), "vitals update changes screen bar ratio")
|
|
entity["dead"] = true
|
|
client.vitals_changed.emit(2)
|
|
await process_frame
|
|
_ck(not bar.visible, "dead entity hides screen bar")
|
|
|
|
entity["dead"] = false
|
|
entity["pos"] = Vector3(0, 0, -12)
|
|
client.vitals_changed.emit(2)
|
|
await process_frame
|
|
_ck(not bar.visible, "entity behind camera hides screen bar")
|
|
|
|
nw.clear_for_map_change()
|
|
_ck(nw._hp_bars.is_empty(), "map change clears screen bars")
|