Files
mtgodot-poc/project/eterngrn_polish_test.gd

147 lines
5.4 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.
# eterngrn_polish_test —— EterGrnLib 对齐打磨(项 3/5/6)的 headless 自检。
# godot --headless --path project --script eterngrn_polish_test.gd
# 项 1/2/4(材质 flag / LOD 淡入 / ease 曲线)是 C++ 内部 + 渲染,离线看不出,走目视。
extends SceneTree
const CharShadow = preload("res://ui/char_shadow.gd")
const PlayerCtl = preload("res://player_controller.gd")
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _init() -> void:
_run()
if _fail == 0:
print("PASS: eterngrn_polish_test")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
# ---- 5. CharShadowDecal + 强制 cast_shadow ----
var root := Node3D.new()
get_root().add_child(root)
var mi := MeshInstance3D.new()
mi.mesh = BoxMesh.new()
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF # 模拟被 world OFF 波及
root.add_child(mi)
CharShadow.attach(root, 1.4)
_ck(mi.cast_shadow == GeometryInstance3D.SHADOW_CASTING_SETTING_ON,
"CharShadow 把 mesh cast_shadow 拉回 ON")
var blob := root.get_node_or_null("BlobShadow")
_ck(blob != null and blob is Decal, "CharShadow 加了 BlobShadow Decal")
_ck(blob != null and blob.texture_albedo != null, "Decal 有径向渐变贴图")
_ck(blob != null and absf(blob.size.x - 1.4) < 0.01, "Decal 足印尺寸 = footprint")
CharShadow.attach(root, 1.4) # 幂等
var n_blob := 0
for c in root.get_children():
if c.name == "BlobShadow": n_blob += 1
_ck(n_blob == 1, "CharShadow.attach 幂等,不重复加 Decal")
# ---- 6. player_controller._ray_pick_t:射线 vs 网格世界 AABB ----
var pc = PlayerCtl.new()
get_root().add_child(pc)
var ent := Node3D.new()
ent.position = Vector3(0, 0, -10)
get_root().add_child(ent)
var emi := MeshInstance3D.new()
var bm := BoxMesh.new()
bm.size = Vector3(2, 2, 2)
emi.mesh = bm
emi.position = Vector3(0, 1, 0) # 盒中心 (0,1,-10)
ent.add_child(emi)
await process_frame
# 从原点朝 -Z 打,正中盒子
var t_hit: float = pc._ray_pick_t(Vector3.ZERO, Vector3(0, 0, -1), ent)
_ck(t_hit > 8.0 and t_hit < 10.0, "_ray_pick_t 命中:进入 t≈9 (got %.2f)" % t_hit)
# 朝 +X 打,不该命中
var t_miss: float = pc._ray_pick_t(Vector3.ZERO, Vector3(1, 0, 0), ent)
_ck(t_miss < 0.0, "_ray_pick_t 未命中返回 -1 (got %.2f)" % t_miss)
# 擦边未命中(偏 5m)
var t_edge: float = pc._ray_pick_t(Vector3(5, 1, 0), Vector3(0, 0, -1), ent)
_ck(t_edge < 0.0, "_ray_pick_t 偏 5m 不命中")
# 无网格 → 回退胶囊近似(PICK_RADIUS 内命中)
var ent2 := Node3D.new()
ent2.position = Vector3(0, 0, -6)
get_root().add_child(ent2)
var t_cap: float = pc._ray_pick_t(Vector3.ZERO, Vector3(0, 0, -1), ent2)
_ck(t_cap > 0.0, "_ray_pick_t 无网格 → 胶囊近似仍能命中 (got %.2f)" % t_cap)
# ---- 3. game_scene._on_local_motion_event 分派 ----
var GS = load("res://game_scene.gd")
var gs = GS.new()
get_root().add_child(gs)
# 桩:cam.shake / fx.spawn_at / _audio.play_at / ui
var calls := {"shake": 0, "spawn": [], "sound": []}
var fake_cam := Camera3D.new()
fake_cam.set_script(_inline_stub("""
extends Camera3D
var log
func shake(s, d=8.0): log["shake"] += 1
"""))
fake_cam.set("log", calls)
var fake_fx = RefCounted.new()
fake_fx.set_script(_inline_stub("""
extends RefCounted
var log
func spawn_at(n, p, gp, o=true): log["spawn"].append(n); return null
"""))
fake_fx.set("log", calls)
var fake_audio := Node.new()
fake_audio.set_script(_inline_stub("""
extends Node
var log
func set_listener(pos, forward=Vector3(0, 0, -1), up=Vector3.UP): pass
func play_character_sound_3d(x, y, z, rel, check_frequency=false): log["sound"].append(rel); return 1
func play_at(rel, pos, parent=null): log["sound"].append(rel)
"""))
fake_audio.set("log", calls)
gs.cam = fake_cam
gs.fx = fake_fx
gs._audio = fake_audio
gs.player = Node3D.new()
gs._mount = Node3D.new()
var fake_client := Node.new()
fake_client.set_script(_inline_stub("""
extends Node
var shots := []
func shoot(skill): shots.append(skill); return true
"""))
get_root().add_child(fake_client)
gs.client = fake_client
get_root().add_child(gs._mount)
gs.ui = CanvasLayer.new()
get_root().add_child(gs.ui)
gs.hud = Node.new()
gs._on_local_motion_event(5, "", "sfx/hit.wav", Vector3.ZERO)
_ck(calls["sound"] == ["sfx/hit.wav"], "motion_event: sound → play_at")
gs._on_local_motion_event(1, "effect/sword_trail.mse", "", Vector3(0, 100, 0))
_ck(calls["spawn"] == ["sword_trail"], "motion_event: effect → fx.spawn_at(basename)")
gs._on_local_motion_event(2, "", "", Vector3.ZERO) # SCREEN_WAVING
_ck(calls["shake"] == 1, "motion_event: type 2 → cam.shake")
gs._on_local_motion_event(3, "", "", Vector3.ZERO) # SCREEN_FLASHING
await process_frame
_ck(gs.ui.get_node_or_null("ColorRect") != null or gs._flash_rect != null,
"motion_event: type 3 → 屏闪 ColorRect")
gs._queue_shot(77)
gs._queue_shot(78)
gs._on_local_motion_event(6, "", "", Vector3.ZERO)
_ck(fake_client.shots == [77], "motion_event: FLY → FIFO pop 一发 CG_SHOOT")
gs._on_local_motion_event(6, "", "", Vector3.ZERO)
_ck(fake_client.shots == [77, 78], "motion_event: FLY 连射按序 popAutoClear")
gs._on_local_motion_event(6, "", "", Vector3.ZERO)
_ck(fake_client.shots == [77, 78], "motion_event: FLY 队列空 → 不再发")
gs.queue_free()
func _inline_stub(src: String) -> GDScript:
var s := GDScript.new()
s.source_code = src
s.reload()
return s