extends SceneTree const NETWORK_NPC_SCENE: PackedScene = preload("res://scenes/characters/network_npc.tscn") func _initialize() -> void: call_deferred("_run") func _run() -> void: var npc := NETWORK_NPC_SCENE.instantiate() root.add_child(npc) npc.call("apply_snapshot", { "npcId": "npc_niulai", "name": "牛来", "scene": "niulai_ambassador", "version": 1, "x": 0, "y": 0, "movementState": "idle", }) var animationPlayer := npc.get_node("AnimationPlayer") as AnimationPlayer assert(npc.collision_layer == 2, "NPC must occupy the dedicated NPC collision layer") assert(npc.collision_mask == 1, "NPC collision mask must include static map collision") var npcShape := (npc.get_node("CollisionShape2D") as CollisionShape2D).shape as RectangleShape2D assert(npcShape.size == Vector2(52, 24), "Niulai must use the adjusted physical footprint") var nameplate := npc.get_node("Nameplate") as Label assert(nameplate.get_theme_font_size("font_size") == 16, "NPC names must use the player name font size") assert(nameplate.get_theme_constant("outline_size") == 3, "NPC names must use the player name outline") assert(nameplate.get_theme_stylebox("normal") is StyleBoxEmpty, "NPC names must not use the old pill background") npc.call("_configure_visual", "town_mayor") assert(is_equal_approx(npc.nameplateOffsetY, -52.0), "The mayor name must follow the visible propeller instead of transparent frame padding") npc.call("_configure_visual", "niulai_ambassador") var library := animationPlayer.get_animation_library("") var expectedRows := {"down": 0, "up": 1, "right": 2, "left": 3} for direction in expectedRows: var animationName := "walk_%s" % direction assert(library.has_animation(animationName), "missing %s" % animationName) var animation := library.get_animation(animationName) assert(animation.loop_mode == Animation.LOOP_LINEAR, "%s must loop" % animationName) assert(animation.track_get_key_count(0) == 4, "%s must contain four frames" % animationName) for column in range(4): assert( animation.track_get_key_value(0, column) == expectedRows[direction] * 4 + column, "%s uses the wrong spritesheet row" % animationName, ) npc.call("interact") assert(root.get_node_or_null("WorldChatBubbleLayer") == null, "NPC interaction must not create a world bubble layer") print("NETWORK_NPC_ANIMATIONS_OK") quit()