Files

171 lines
7.3 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.
# p9_test —— P9:小地图 + 昼夜 + 天气 + 传送 headless 自检。
# godot --headless --path project --script p9_test.gd
extends SceneTree
const Minimap = preload("res://ui/minimap.gd")
const WorldTime = preload("res://world/world_time.gd")
const Weather = preload("res://fx/weather.gd")
class FakeClient extends Node:
signal time_changed(server_epoch: int)
signal channel_changed(channel: int)
signal npc_marks_changed()
signal land_areas_changed()
signal observer_event(kind: int, vid: int, pos: Vector2)
signal world_reset()
signal world_markers_changed()
signal warp(pos: Vector3, same_server: bool)
var _time := 0
var entities := []
var npc_marks := []
var markers := []
var observers := []
var land_areas := []
var party := []
func get_server_time() -> int: return _time
func get_entities() -> Array: return entities
func get_npc_marks() -> Array: return npc_marks
func get_world_markers() -> Array: return markers
func get_observers() -> Array: return observers
func get_land_areas() -> Array: return land_areas
func get_party() -> Array: return party
func get_channel() -> int: return 3
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: p9_test (minimap + world_time + weather + warp)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
var canvas := CanvasLayer.new()
get_root().add_child(canvas)
var fc := FakeClient.new()
get_root().add_child(fc)
var player := Node3D.new()
player.position = Vector3.ZERO
get_root().add_child(player)
# --- 小地图 ---
var mm: Node = Minimap.new()
get_root().add_child(mm)
mm.setup(fc, canvas, func() -> Node3D: return player)
_ck(mm._view != null, "minimap: view built")
fc.channel_changed.emit(7)
_ck(mm._ch_label.text == "CH 7", "minimap: channel label")
# _to_map: 北(z 100m)应在圆心正上方附近;夹到半径内
var north: Vector2 = mm._to_map(Vector3(0, 0, -1000), Vector3.ZERO)
_ck(north.x == mm.RADIUS and north.y < mm.RADIUS, "minimap: north maps up, clamped")
var east: Vector2 = mm._to_map(Vector3(10, 0, 0), Vector3.ZERO)
_ck(east.x > mm.RADIUS and abs(east.y - mm.RADIUS) < 0.01, "minimap: east maps right")
var base_scale: float = mm.get_scale()
mm.set_scale(base_scale * 2.0)
var zoomed_east: Vector2 = mm._to_map(Vector3(10, 0, 0), Vector3.ZERO)
_ck(mm.get_scale() > base_scale and zoomed_east.x > east.x, "minimap: scale zooms world projection")
var wheel_scale: float = mm.get_scale()
var wheel := InputEventMouseButton.new()
wheel.button_index = MOUSE_BUTTON_WHEEL_DOWN
wheel.pressed = true
mm._on_gui_input(wheel)
_ck(mm.get_scale() < wheel_scale, "minimap: wheel down zooms out")
fc.entities = [{"vid": 11, "is_main": false, "ch_type": 0, "pos": Vector3(5, 0, 5)}]
fc.party = [{"vid": 11, "leader": true}]
fc.npc_marks = [{"type": 2, "name": "Gate", "pos": Vector3(3, 0, 3)}]
fc.markers = [{"id": 1, "name": "Quest", "pos": Vector3(-4, 0, -4)}]
fc.observers = [{"vid": 9001, "pos": Vector3(2, 0, -2)}]
fc.land_areas = [{"id": 42, "guild_id": 77, "x": -200, "y": -200, "width": 400, "height": 400}]
# ClientVS22's CPythonMiniMap interpolates observer moves for 1 second.
fc.observer_event.emit(mm.OBSERVER_ADD, 9001, Vector2(0, 0))
var observer_start: Vector3 = mm._observer_position(9001, Vector3.ZERO)
_ck(observer_start.distance_to(Vector3.ZERO) < 0.01, "minimap: observer add seeds current position")
fc.observer_event.emit(mm.OBSERVER_MOVE, 9001, Vector2(10, 4))
var observer_immediate: Vector3 = mm._observer_position(9001, Vector3.ZERO)
_ck(observer_immediate.distance_to(Vector3.ZERO) < 0.1, "minimap: observer move starts at current position")
await create_timer(1.05).timeout
var observer_final: Vector3 = mm._observer_position(9001, Vector3.ZERO)
_ck(observer_final.distance_to(Vector3(10, 0, 4)) < 0.1, "minimap: observer move reaches destination")
fc.observer_event.emit(mm.OBSERVER_REMOVE, 9001, Vector2.ZERO)
_ck(not mm._observer_tracks.has(9001), "minimap: observer remove clears track")
fc.observer_event.emit(mm.OBSERVER_ADD, 9001, Vector2(2, -2))
fc.world_reset.emit()
_ck(mm._observer_tracks.is_empty(), "minimap: world reset clears observer tracks")
mm._view.queue_redraw()
await process_frame
_ck(mm._view.is_visible_in_tree(), "minimap: redraw scheduled, view live")
# --- 任务信标:圆内画星,越出半径贴圆周画方向箭头(§5.2 世界箭头 / 屏幕边缘指示)---
var rim: float = mm.RADIUS - mm.SIGNAL_RIM_INSET
var near_geo: Dictionary = mm.signal_marker_geometry(Vector2(10, 0), rim)
_ck(not near_geo["clamped"] and near_geo["offset"] == Vector2(10, 0),
"minimap signal: inside rim keeps true offset")
var far_geo: Dictionary = mm.signal_marker_geometry(Vector2(400, 0), rim)
_ck(far_geo["clamped"] and abs(far_geo["offset"].length() - rim) < 0.01,
"minimap signal: outside rim clamps to circle")
_ck(abs(far_geo["angle"]) < 0.001 and abs(far_geo["offset"].x - rim) < 0.01,
"minimap signal: clamp keeps bearing to target (east)")
var diag_geo: Dictionary = mm.signal_marker_geometry(Vector2(-300, -300), rim)
_ck(diag_geo["clamped"] and abs(diag_geo["angle"] - atan2(-300.0, -300.0)) < 0.001
and abs(diag_geo["offset"].length() - rim) < 0.01,
"minimap signal: diagonal clamp preserves atan2 bearing")
var edge_geo: Dictionary = mm.signal_marker_geometry(Vector2(rim, 0), rim)
_ck(edge_geo["clamped"], "minimap signal: exactly on rim counts as clamped (>=)")
mm.add_signal_point(120000.0, 90000.0)
_ck(mm.signal_point_count() == 1, "minimap signal: add_signal_point stores waypoint")
mm._view.queue_redraw()
await process_frame
mm.clear_signal_points()
_ck(mm.signal_point_count() == 0, "minimap signal: clear_signal_points empties waypoints")
# --- 昼夜 ---
var sun := DirectionalLight3D.new()
get_root().add_child(sun)
var env := Environment.new()
env.background_mode = Environment.BG_COLOR
var wt: Node = WorldTime.new()
get_root().add_child(wt)
wt.setup(fc, sun, env)
# 正午:2025-01-01 12:00:00 UTC = 1735732800
fc._time = 1735732800
fc.time_changed.emit(1735732800)
_ck(abs(wt.day_fraction() - 0.5) < 0.01, "world_time: noon -> day_fraction ~0.5")
wt._process(0.016)
var noon_energy: float = sun.light_energy
_ck(noon_energy > 0.9, "world_time: noon sun bright (%.2f)" % noon_energy)
# 午夜
fc.time_changed.emit(1735732800 - 43200)
_ck(abs(wt.day_fraction()) < 0.01 or abs(wt.day_fraction() - 1.0) < 0.01,
"world_time: midnight -> fraction ~0")
wt._process(0.016)
_ck(sun.light_energy < 0.2, "world_time: midnight sun dark (%.3f)" % sun.light_energy)
# --- 天气 ---
var cam := Camera3D.new()
get_root().add_child(cam)
var w: Node3D = Weather.new()
get_root().add_child(w)
w.setup(cam)
_ck(w.kind() == "none" and not w._p.emitting, "weather: starts off")
w.set_weather("snow")
_ck(w._p.emitting and w.kind() == "snow", "weather: snow on")
var g_snow: Vector3 = w._p.process_material.gravity
w.set_weather("rain")
var g_rain: Vector3 = w._p.process_material.gravity
_ck(g_rain.y < g_snow.y, "weather: rain falls faster than snow")
w.set_weather("none")
_ck(not w._p.emitting, "weather: off again")
# --- 传送信号(同服)---
var got := []
fc.warp.connect(func(pos, same): got.append([pos, same]))
fc.warp.emit(Vector3(50, 0, -30), true)
_ck(got.size() == 1 and got[0][1] == true, "warp: same-server signal received")