extends Node class ScriptNode: var scene func step_type(): return "" func get_scene(): return scene func init(scene): print("starting step " + step_type()) self.scene = scene pass func do(delta): pass func is_done(): return true func interrupt(): return false func handle_input(event: InputEvent) -> bool: return false class Say: extends ScriptNode var text var subject : Node2D = null var done = false var stop = true var started = false var t func step_type(): return "Say " + text func init(scene): super(scene) #subject.play_animation("talk", 0) func do(delta): if !started: started = true t = Timer.new() t.autostart = true t.wait_time = 5.0 subject.add_child(t) var label: Label = subject.find_child("talk-label") #label.set_text(text) #subject.find_child("label-root").show() await scene.find_child("dialogue").say(text) await t.timeout subject.remove_child(t) if stop: await scene.find_child("dialogue").done_say() #subject.find_child("label-root").hide() #subject.stop_animation() #subject.play_animation_raw("idle_" + subject.facing) done = true started = true func is_done(): return done func interrupt(): done = true if stop: await scene.find_child("dialogue").done_say() subject.stop_animation() subject.play_animation_raw("idle_" + subject.facing) subject.find_child("label-root").hide() class Narrate: extends ScriptNode var text var done = false var started = false var dismissed_by_click = false var fade_in_duration = 0.3 var fade_out_duration = 0.3 var chars_per_second = 20.0 # Reading speed var min_duration = 2.0 var max_duration = 10.0 func step_type(): return "Narrate " + text.substr(0, 20) func calculate_duration() -> float: var duration = text.length() / chars_per_second return clamp(duration, min_duration, max_duration) func do(delta): if !started: started = true dismissed_by_click = false # Show narrator overlay with fade in var narrator_overlay = scene.find_child("NarratorOverlay") if narrator_overlay: var panel = narrator_overlay.find_child("Panel") var label = panel.find_child("Label") if label: label.text = text if panel: panel.modulate = Color(1, 1, 1, 0) panel.show() # Fade in var tween = scene.create_tween() tween.tween_property(panel, "modulate", Color(1, 1, 1, 1), fade_in_duration) await tween.finished # Set global flag that narrator is active scene.set_meta("narrator_active", true) # Calculate display duration var display_duration = calculate_duration() # Wait for either timeout or click var timer = scene.get_tree().create_timer(display_duration) while timer.time_left > 0 and not dismissed_by_click: await scene.get_tree().process_frame # Fade out (keep narrator_active flag true during fade to prevent walk trigger) var overlay = scene.find_child("NarratorOverlay") if overlay: var p = overlay.find_child("Panel") if p and p.visible: var tween = scene.create_tween() tween.tween_property(p, "modulate", Color(1, 1, 1, 0), fade_out_duration) await tween.finished p.hide() # Clear global flag AFTER fade completes scene.set_meta("narrator_active", false) done = true started = true func is_done(): return done func dismiss(): dismissed_by_click = true func interrupt(): done = true var overlay = scene.find_child("NarratorOverlay") if overlay: var p = overlay.find_child("Panel") if p: p.hide() scene.set_meta("narrator_active", false) func handle_input(event: InputEvent) -> bool: if event is InputEventMouseButton and Input.is_action_just_released("interact"): if not dismissed_by_click and not done: dismissed_by_click = true return true return false class WalkTo: extends ScriptNode var path = [] var subject : Node2D = null var script_fn = null func step_type(): return "WalkTo " + subject.get_name() func do(delta): # Calculate the movement distance for this frame var distance_to_walk = 1500 * delta * (subject.get_transform().get_scale().y) var p = subject.position if path.size()> 0: # Move the player along the path until he has run out of movement or the path ends. while distance_to_walk > 0 and path.size() > 0: var dir = snapped(subject.position.angle_to_point(path[0]), PI/4) / (PI/4) dir = wrapi(dir, 0, 8) var anim # seek = animation_player.current_animation_position #animation_player.play(anim) #animation_player.seek(seek, true) #subject.find_node("AnimationPlayer").playback_speed=1.8 var distance_to_next_point = p.distance_to(path[0]) if distance_to_walk <= distance_to_next_point: var facing = "" if dir == 4: facing="W" if dir == 5: facing ="NW" elif dir == 6: facing="N" elif dir ==7: facing="NE" elif dir == 0: facing="E" elif dir == 1: facing="SE" elif dir == 2: facing = "S" elif dir == 3: facing = "SW" if facing: var seek = 0 subject.facing = facing var sprite = subject.play_animation_maintain("walk") # The player does not have enough movement left to get to the next point. p += p.direction_to(path[0]) * distance_to_walk else: # The player get to the next point p = path[0] path = path.slice(1) # Update the distance to walk distance_to_walk -= distance_to_next_point subject.position = p; func is_done(): if path.size() == 0: subject.play_animation("idle", 0) #subject.find_child("AnimatedSprite2D").pause() return path.size() == 0 class WalkToDeferred: extends WalkTo var named_to var named_from var subject_name func step_type(): return "Walk to " + named_to func init(scene): super(scene) print("SCENE IS", scene) subject = scene.get_node(subject_name) var s = scene.get_node("SceneViewport/background") var g = s.get_children() path = NavigationServer2D.map_get_path(s.map, s.pathfind.to_local(s.get_node(named_from).global_position), s.pathfind.to_local(s.get_node(named_to).global_position), true) print(" PATH IS", path) #[Vector2(0,0), Vector2(300,300)] class Transition: extends ScriptNode var path = [] var subject : Node2D = null var next : Resource = null var script_fn = null var position = null var transitioned : String var running = false var done = false func step_type(): return "transition" func do(delta): if not running: running = true var parent = subject.get_parent() var fade_player = subject.get_parent().get_parent().find_child("FadePlayer", true) fade_player.play("fade-out",0.0, 1.5) await fade_player.animation_finished var new_scene = next.instantiate() new_scene.name="background" parent.remove_child(subject) var pos : Node2D = new_scene.get_node(position) var c = new_scene.get_children() if new_scene is Scene: new_scene.starting_point = pos.global_position parent.add_child(new_scene) subject.emit_signal("transitioned", new_scene) subject.queue_free() fade_player.play("fade-in",0.0, 1.5) await fade_player.animation_finished done = true parent func is_done(): return done class TriggerSignal: extends ScriptNode var path = [] var subject : Node2D = null var next : Resource = null var script_fn = null var running = false var s :String = "" func do(delta): pass # get_tree().find_child("Node2D/background").emit_signal(s) func is_done(): true class SwitchCamera: extends ScriptNode var path = "" var d = false var t: SceneTreeTimer func init(scene: Node2D): var s = scene.get_node("SceneViewport/background") var g = s.get_children() CameraTransition.transition_camera2D(scene.get_tree().root.get_node("Node2D/SceneViewport/PlayerTracker/Camera2D"), s.find_child(path)) t = scene.get_tree().create_timer(2.0) func do(delta): await t.timeout d = true # get_tree().find_child("Node2D/background").emit_signal(s) func is_done(): return d class ResetCamera: extends ScriptNode var path = "" var d = false var t: SceneTreeTimer func init(scene: Node2D): var s = scene.get_node("SceneViewport/background") CameraTransition.transition_camera2D(scene.get_viewport().get_camera_2d(), scene.get_node("SceneViewport/PlayerTracker/Camera2D")) t = scene.get_tree().create_timer(2.0) func do(delta): await t.timeout d = true # get_tree().find_child("Node2D/background").emit_signal(s) func is_done(): return d class ScriptEdge: var from : ScriptNode var to : ScriptNode class ScriptGraph: var initial: ScriptNode var current = [] var nodes = [] var edges = [] var can_interrupt = false var current_scene var last_node signal script_complete func step(delta, scene): for c in range(current.size()): current[c].do(delta) var to_add = [] for current_index in range(current.size()): var current_step = current[-current_index] if current_step.is_done(): print("Stopping step", current_step.step_type()) current.remove_at(-current_index) for edge in edges: if edge.from == current_step: to_add.push_back(edge.to) edge.to.init(scene) current.append_array(to_add) if current.size() == 0: emit_signal("script_complete") func interrupt(): for c in current: c.interrupt() return can_interrupt func handle_input(event: InputEvent) -> bool: for c in current: if c.handle_input(event): return true return false var script_graph: ScriptGraph #var last_node: ScriptNode func init(step: ScriptNode): script_graph = ScriptGraph.new() script_graph.initial = step step.init(get_node("/root/Node2D")) script_graph.current = [step] script_graph.last_node = step #last_node = step return self func and_then(step: ScriptNode): script_graph.nodes.push_back(step) var edge = ScriptEdge.new() edge.from = script_graph.last_node edge.to = step script_graph.edges.push_back(edge) script_graph.last_node=step return self func can_interrupt(): script_graph.can_interrupt = true return self func build(subject, on_complete): script_graph.connect("script_complete", Callable(subject, on_complete)) script_graph.connect("script_complete", self.on_complete) return script_graph func on_complete(): current_script=null func walk_path(subject, path): var walk_to = WalkTo.new() walk_to.subject = subject walk_to.path = path return walk_to func say(subject, text, stop=true): var say = Say.new() say.subject = subject say.text = text say.stop = stop return say func narrate(text): var narrate = Narrate.new() narrate.text = text return narrate func dismiss_current_narrator(): if current_script: for node in current_script.current: if node is Narrate: node.dismiss() return true return false func walk_to_deferred(named_from, named_to): var say = WalkToDeferred.new() say.subject_name = "SceneViewport/background/Graham" say.named_from = named_from say.named_to = named_to return say func trigger_signal(s): var thing = TriggerSignal.new() thing.s = s return thing func transition(subject, scene, position_identifier): var t = Transition.new() t.subject = subject t.next = scene t.position = position_identifier return t func switch_camera(path): var thing = SwitchCamera.new() thing.path = path return thing func reset_camera(): var thing = ResetCamera.new() return thing var current_script : ScriptGraph = null