progresS
This commit is contained in:
46
Game.tscn
46
Game.tscn
@@ -72,6 +72,19 @@ font_size = 32
|
|||||||
outline_size = 5
|
outline_size = 5
|
||||||
outline_color = Color(0, 0, 0, 1)
|
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]
|
[node name="Node2D" type="Node2D" unique_id=826166852]
|
||||||
script = ExtResource("2")
|
script = ExtResource("2")
|
||||||
|
|
||||||
@@ -136,3 +149,36 @@ script = ExtResource("5_rglkg")
|
|||||||
[node name="dialogue" parent="." unique_id=186154081 instance=ExtResource("7_fj12q")]
|
[node name="dialogue" parent="." unique_id=186154081 instance=ExtResource("7_fj12q")]
|
||||||
position = Vector2(328, 106)
|
position = Vector2(328, 106)
|
||||||
scale = Vector2(0.75, 0.75)
|
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
|
||||||
|
|||||||
112
GameScript.gd
112
GameScript.gd
@@ -21,6 +21,9 @@ class ScriptNode:
|
|||||||
func interrupt():
|
func interrupt():
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
func handle_input(event: InputEvent) -> bool:
|
||||||
|
return false
|
||||||
|
|
||||||
class Say:
|
class Say:
|
||||||
extends ScriptNode
|
extends ScriptNode
|
||||||
var text
|
var text
|
||||||
@@ -73,6 +76,96 @@ class Say:
|
|||||||
subject.find_child("label-root").hide()
|
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:
|
class WalkTo:
|
||||||
extends ScriptNode
|
extends ScriptNode
|
||||||
var path = []
|
var path = []
|
||||||
@@ -293,6 +386,12 @@ class ScriptGraph:
|
|||||||
c.interrupt()
|
c.interrupt()
|
||||||
return can_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 script_graph: ScriptGraph
|
||||||
#var last_node: ScriptNode
|
#var last_node: ScriptNode
|
||||||
@@ -340,6 +439,19 @@ func say(subject, text, stop=true):
|
|||||||
say.stop = stop
|
say.stop = stop
|
||||||
return say
|
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):
|
func walk_to_deferred(named_from, named_to):
|
||||||
var say = WalkToDeferred.new()
|
var say = WalkToDeferred.new()
|
||||||
say.subject_name = "SceneViewport/background/Graham"
|
say.subject_name = "SceneViewport/background/Graham"
|
||||||
|
|||||||
28
MainGame.gd
28
MainGame.gd
@@ -1,11 +1,11 @@
|
|||||||
extends Node2D
|
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 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:
|
func get_scene() -> Scene:
|
||||||
return $SceneViewport/background
|
return $SceneViewport/background
|
||||||
|
|
||||||
@@ -51,12 +51,30 @@ func _process(delta):
|
|||||||
var s = get_scene().ego_scale(player)
|
var s = get_scene().ego_scale(player)
|
||||||
player.scale = Vector2(s, s)
|
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:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
$SceneViewport.push_input(event)
|
$SceneViewport.push_input(event)
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event.is_action_released("quit"):
|
if event.is_action_released("quit"):
|
||||||
get_tree().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
|
ActionState.current_action = (ActionState.current_action + 1) % 4
|
||||||
Input.set_custom_mouse_cursor(cursors[ActionState.current_action])
|
Input.set_custom_mouse_cursor(cursors[ActionState.current_action])
|
||||||
|
|||||||
11
Scene.gd
11
Scene.gd
@@ -123,18 +123,21 @@ func start_main_script(s):
|
|||||||
ScriptBuilder.current_script = s
|
ScriptBuilder.current_script = s
|
||||||
|
|
||||||
func _unhandled_input(event):
|
func _unhandled_input(event):
|
||||||
if event is InputEventMouseButton and event.is_action("interact"):
|
if event is InputEventMouseButton and Input.is_action_just_released("interact"):
|
||||||
print (ego.position, pathfind.to_local(get_global_mouse_position()))
|
# 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
|
# If look cursor is active and we got here, no SetPiece handled the input
|
||||||
# so this is a room-wide look
|
# so this is a room-wide look
|
||||||
if ActionState.current_action == ActionState.Action.LOOK:
|
if ActionState.current_action == ActionState.Action.LOOK:
|
||||||
_on_room_looked()
|
_on_room_looked()
|
||||||
return
|
return
|
||||||
|
if ActionState.current_action == ActionState.Action.WALK:
|
||||||
var path = NavigationServer2D.map_get_path(map, ego.position, pathfind.to_local(get_global_mouse_position()), true)
|
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"))
|
start_main_script(ScriptBuilder.init(ScriptBuilder.walk_path(ego, path)).can_interrupt().build(self, "_on_script_complete"))
|
||||||
pass
|
|
||||||
|
|
||||||
func _on_room_looked() -> void:
|
func _on_room_looked() -> void:
|
||||||
# Default room look description - override in room scripts
|
# Default room look description - override in room scripts
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ func _process(delta):
|
|||||||
if Geometry2D.is_point_in_polygon(to_local(get_global_mouse_position()), self.polygon):
|
if Geometry2D.is_point_in_polygon(to_local(get_global_mouse_position()), self.polygon):
|
||||||
if is_in == false:
|
if is_in == false:
|
||||||
is_in = true
|
is_in = true
|
||||||
print("ENTERED0", label)
|
|
||||||
emit_signal("entered", label)
|
emit_signal("entered", label)
|
||||||
else:
|
else:
|
||||||
if is_in == true:
|
if is_in == true:
|
||||||
@@ -72,7 +71,12 @@ func _get_overlapping_setpieces() -> Array[SetPiece]:
|
|||||||
func _input(event):
|
func _input(event):
|
||||||
if not Engine.is_editor_hint():
|
if not Engine.is_editor_hint():
|
||||||
if Geometry2D.is_point_in_polygon(to_local(get_global_mouse_position()), self.polygon):
|
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
|
# Find all overlapping SetPieces and check if this one has highest priority
|
||||||
var overlapping = _get_overlapping_setpieces()
|
var overlapping = _get_overlapping_setpieces()
|
||||||
overlapping.append(self)
|
overlapping.append(self)
|
||||||
|
|||||||
BIN
hourglass_icon.png
LFS
Normal file
BIN
hourglass_icon.png
LFS
Normal file
Binary file not shown.
40
hourglass_icon.png.import
Normal file
40
hourglass_icon.png.import
Normal file
@@ -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
|
||||||
1
hourglass_icon.png.uid
Normal file
1
hourglass_icon.png.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://f4bd88e46ad74
|
||||||
2
label.gd
2
label.gd
@@ -65,8 +65,6 @@ func _process(delta):
|
|||||||
mouse_pos.x = clamp(mouse_pos.x, cam_top_left.x + margin, cam_bottom_right.x - label.size.x - margin)
|
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)
|
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
|
# Update label position
|
||||||
global_position = mouse_pos
|
global_position = mouse_pos
|
||||||
|
|||||||
@@ -19,29 +19,29 @@ func _on_forest_path_27_interacted() -> void:
|
|||||||
|
|
||||||
func _on_pool_looked() -> void:
|
func _on_pool_looked() -> void:
|
||||||
start_main_script(ScriptBuilder.init(
|
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"))
|
).build(self, "_on_script_complete"))
|
||||||
|
|
||||||
|
|
||||||
func _on_columns_looked() -> void:
|
func _on_columns_looked() -> void:
|
||||||
start_main_script(ScriptBuilder.init(
|
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"))
|
).build(self, "_on_script_complete"))
|
||||||
|
|
||||||
|
|
||||||
func _on_stairs_looked() -> void:
|
func _on_stairs_looked() -> void:
|
||||||
start_main_script(ScriptBuilder.init(
|
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"))
|
).build(self, "_on_script_complete"))
|
||||||
|
|
||||||
|
|
||||||
func _on_ground_looked() -> void:
|
func _on_ground_looked() -> void:
|
||||||
start_main_script(ScriptBuilder.init(
|
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"))
|
).build(self, "_on_script_complete"))
|
||||||
|
|
||||||
|
|
||||||
func _on_room_looked() -> void:
|
func _on_room_looked() -> void:
|
||||||
start_main_script(ScriptBuilder.init(
|
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"))
|
).build(self, "_on_script_complete"))
|
||||||
|
|||||||
Reference in New Issue
Block a user