From 7203c843ec480c011f6b912d2d3a3f6a613e54b4 Mon Sep 17 00:00:00 2001 From: Bryce Date: Mon, 9 Mar 2026 11:57:48 -0700 Subject: [PATCH] progresS --- Game.tscn | 46 +++++++ GameScript.gd | 112 ++++++++++++++++++ MainGame.gd | 28 ++++- Scene.gd | 15 ++- SetPiece_.gd | 8 +- hourglass_icon.png | 3 + hourglass_icon.png.import | 40 +++++++ hourglass_icon.png.uid | 1 + label.gd | 4 +- .../kq4_003_fountain_pool.gd | 10 +- 10 files changed, 246 insertions(+), 21 deletions(-) create mode 100644 hourglass_icon.png create mode 100644 hourglass_icon.png.import create mode 100644 hourglass_icon.png.uid diff --git a/Game.tscn b/Game.tscn index 69126b4..d34d076 100644 --- a/Game.tscn +++ b/Game.tscn @@ -72,6 +72,19 @@ font_size = 32 outline_size = 5 outline_color = Color(0, 0, 0, 1) +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_narrator"] +bg_color = Color(0, 0, 0, 0.75) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="LabelSettings" id="LabelSettings_narrator"] +font = ExtResource("5_0olt8") +font_size = 28 +outline_size = 4 +outline_color = Color(0, 0, 0, 1) + [node name="Node2D" type="Node2D" unique_id=826166852] script = ExtResource("2") @@ -136,3 +149,36 @@ script = ExtResource("5_rglkg") [node name="dialogue" parent="." unique_id=186154081 instance=ExtResource("7_fj12q")] position = Vector2(328, 106) scale = Vector2(0.75, 0.75) + +[node name="NarratorOverlay" type="CanvasLayer" parent="." unique_id=987654321] +layer = 10 + +[node name="Panel" type="Panel" parent="NarratorOverlay" unique_id=987654322] +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -600.0 +offset_top = -120.0 +offset_right = 600.0 +offset_bottom = -20.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_override_styles/panel = SubResource("StyleBoxFlat_narrator") + +[node name="Label" type="Label" parent="NarratorOverlay/Panel" unique_id=987654323] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 20.0 +offset_top = 15.0 +offset_right = -20.0 +offset_bottom = -15.0 +grow_horizontal = 2 +grow_vertical = 2 +label_settings = SubResource("LabelSettings_narrator") +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 3 diff --git a/GameScript.gd b/GameScript.gd index 6658eab..33a4b09 100644 --- a/GameScript.gd +++ b/GameScript.gd @@ -20,6 +20,9 @@ class ScriptNode: func interrupt(): return false + + func handle_input(event: InputEvent) -> bool: + return false class Say: extends ScriptNode @@ -71,6 +74,96 @@ class 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: @@ -292,6 +385,12 @@ class ScriptGraph: 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 @@ -340,6 +439,19 @@ func say(subject, text, stop=true): 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" diff --git a/MainGame.gd b/MainGame.gd index 64fbc4e..4b2c890 100644 --- a/MainGame.gd +++ b/MainGame.gd @@ -1,11 +1,11 @@ extends Node2D -# Declare member variables here. Examples: -# var a = 2 -# var b = "text" var cursors = [load("res://boot_icon.png"), load("res://eye_icon.png"), load("res://hand_icon.png"), load("res://speech_icon.png")] -var cursor_index = 0 +var hourglass_cursor = load("res://hourglass_icon.png") +var previous_cursor_index: int = 0 +var is_script_running: bool = false +var is_cursor_locked: bool = false # When true, hourglass is shown and cursor can't be changed func get_scene() -> Scene: return $SceneViewport/background @@ -51,12 +51,30 @@ func _process(delta): var s = get_scene().ego_scale(player) player.scale = Vector2(s, s) + # Update cursor if script state changed + var script = $GameScript.current_script + if script and not is_script_running: + set_script_cursor() + elif not script and is_script_running: + restore_cursor() + +func set_script_cursor() -> void: + previous_cursor_index = ActionState.current_action + is_script_running = true + is_cursor_locked = true # Lock cursor to hourglass + Input.set_custom_mouse_cursor(hourglass_cursor) + +func restore_cursor() -> void: + is_script_running = false + is_cursor_locked = false # Unlock cursor + Input.set_custom_mouse_cursor(cursors[previous_cursor_index]) + func _unhandled_input(event: InputEvent) -> void: $SceneViewport.push_input(event) func _input(event): if event.is_action_released("quit"): get_tree().quit() - if event.is_action_released("right_click"): + if event.is_action_released("right_click") and not is_cursor_locked: ActionState.current_action = (ActionState.current_action + 1) % 4 Input.set_custom_mouse_cursor(cursors[ActionState.current_action]) diff --git a/Scene.gd b/Scene.gd index 98ed5b5..43a1b7a 100644 --- a/Scene.gd +++ b/Scene.gd @@ -123,18 +123,21 @@ func start_main_script(s): ScriptBuilder.current_script = s func _unhandled_input(event): - if event is InputEventMouseButton and event.is_action("interact"): - print (ego.position, pathfind.to_local(get_global_mouse_position())) + if event is InputEventMouseButton and Input.is_action_just_released("interact"): + # If a script is running and can't be interrupted, delegate to it + if ScriptBuilder.current_script and not ScriptBuilder.current_script.can_interrupt: + if ScriptBuilder.current_script.handle_input(event): + return + var root = get_node("/root/Node2D") # If look cursor is active and we got here, no SetPiece handled the input # so this is a room-wide look if ActionState.current_action == ActionState.Action.LOOK: _on_room_looked() return - - var path = NavigationServer2D.map_get_path(map, ego.position, pathfind.to_local(get_global_mouse_position()), true) - start_main_script(ScriptBuilder.init(ScriptBuilder.walk_path(ego, path)).can_interrupt().build(self, "_on_script_complete")) - pass + if ActionState.current_action == ActionState.Action.WALK: + var path = NavigationServer2D.map_get_path(map, ego.position, pathfind.to_local(get_global_mouse_position()), true) + start_main_script(ScriptBuilder.init(ScriptBuilder.walk_path(ego, path)).can_interrupt().build(self, "_on_script_complete")) func _on_room_looked() -> void: # Default room look description - override in room scripts diff --git a/SetPiece_.gd b/SetPiece_.gd index 9b18b6d..ab4537a 100644 --- a/SetPiece_.gd +++ b/SetPiece_.gd @@ -49,7 +49,6 @@ func _process(delta): if Geometry2D.is_point_in_polygon(to_local(get_global_mouse_position()), self.polygon): if is_in == false: is_in = true - print("ENTERED0", label) emit_signal("entered", label) else: if is_in == true: @@ -72,7 +71,12 @@ func _get_overlapping_setpieces() -> Array[SetPiece]: func _input(event): if not Engine.is_editor_hint(): if Geometry2D.is_point_in_polygon(to_local(get_global_mouse_position()), self.polygon): - if event is InputEventMouseButton and event.is_action("interact"): + if event is InputEventMouseButton and Input.is_action_just_released("interact"): + # Check if a script is running that shouldn't be interrupted + var script_builder = get_node_or_null("/root/Node2D/GameScript") + if script_builder and script_builder.current_script and not script_builder.current_script.can_interrupt: + return # Let the script handle the input + # Find all overlapping SetPieces and check if this one has highest priority var overlapping = _get_overlapping_setpieces() overlapping.append(self) diff --git a/hourglass_icon.png b/hourglass_icon.png new file mode 100644 index 0000000..0b4d74e --- /dev/null +++ b/hourglass_icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c160bfdb8d0423b958083202dc7b58d499cbef22f28d2a58626884378ce9b7f +size 3305 diff --git a/hourglass_icon.png.import b/hourglass_icon.png.import new file mode 100644 index 0000000..25381ed --- /dev/null +++ b/hourglass_icon.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b8vin6hvl2ihd" +path="res://.godot/imported/hourglass_icon.png-8d78a8fc9e1e39f47135f2bd27e87cd0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://hourglass_icon.png" +dest_files=["res://.godot/imported/hourglass_icon.png-8d78a8fc9e1e39f47135f2bd27e87cd0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/hourglass_icon.png.uid b/hourglass_icon.png.uid new file mode 100644 index 0000000..8cea50f --- /dev/null +++ b/hourglass_icon.png.uid @@ -0,0 +1 @@ +uid://f4bd88e46ad74 diff --git a/label.gd b/label.gd index 8dfa106..bf64a6b 100644 --- a/label.gd +++ b/label.gd @@ -64,9 +64,7 @@ func _process(delta): var s = label.size mouse_pos.x = clamp(mouse_pos.x, cam_top_left.x + margin, cam_bottom_right.x - label.size.x - margin) mouse_pos.y = clamp(mouse_pos.y, cam_top_left.y, cam_bottom_right.y) - - if $label.visible: - print("LABEL VISIBLE", $label.text) + # Update label position global_position = mouse_pos diff --git a/scenes/kq4_003_fountain_pool/kq4_003_fountain_pool.gd b/scenes/kq4_003_fountain_pool/kq4_003_fountain_pool.gd index 98522f8..670b8af 100644 --- a/scenes/kq4_003_fountain_pool/kq4_003_fountain_pool.gd +++ b/scenes/kq4_003_fountain_pool/kq4_003_fountain_pool.gd @@ -19,29 +19,29 @@ func _on_forest_path_27_interacted() -> void: func _on_pool_looked() -> void: start_main_script(ScriptBuilder.init( - ScriptBuilder.say(ego, "The beautiful pool is lined with tall marble columns. Its crystal clear water looks very inviting.") + ScriptBuilder.narrate("The beautiful pool is lined with tall marble columns. Its crystal clear water looks very inviting.") ).build(self, "_on_script_complete")) func _on_columns_looked() -> void: start_main_script(ScriptBuilder.init( - ScriptBuilder.say(ego, "The marble columns flank the lovely pool.") + ScriptBuilder.narrate("The marble columns flank the lovely pool.") ).build(self, "_on_script_complete")) func _on_stairs_looked() -> void: start_main_script(ScriptBuilder.init( - ScriptBuilder.say(ego, "There are steps at the north end of the pool.") + ScriptBuilder.narrate("There are steps at the north end of the pool.") ).build(self, "_on_script_complete")) func _on_ground_looked() -> void: start_main_script(ScriptBuilder.init( - ScriptBuilder.say(ego, "You see nothing of importance on the ground.") + ScriptBuilder.narrate("You see nothing of importance on the ground.") ).build(self, "_on_script_complete")) func _on_room_looked() -> void: start_main_script(ScriptBuilder.init( - ScriptBuilder.say(ego, "The beautiful pool, with its elegant marble columns, has a wonderful setting in these woods. The water looks so cool and inviting; you're almost tempted to jump in.") + ScriptBuilder.narrate("The beautiful pool, with its elegant marble columns, has a wonderful setting in these woods. The water looks so cool and inviting; you're almost tempted to jump in.") ).build(self, "_on_script_complete"))