progresS
This commit is contained in:
46
Game.tscn
46
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
|
||||
|
||||
112
GameScript.gd
112
GameScript.gd
@@ -21,6 +21,9 @@ class ScriptNode:
|
||||
func interrupt():
|
||||
return false
|
||||
|
||||
func handle_input(event: InputEvent) -> bool:
|
||||
return false
|
||||
|
||||
class Say:
|
||||
extends ScriptNode
|
||||
var text
|
||||
@@ -73,6 +76,96 @@ class Say:
|
||||
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 = []
|
||||
@@ -293,6 +386,12 @@ class ScriptGraph:
|
||||
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
|
||||
@@ -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"
|
||||
|
||||
28
MainGame.gd
28
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])
|
||||
|
||||
15
Scene.gd
15
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
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.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
|
||||
|
||||
@@ -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"))
|
||||
|
||||
Reference in New Issue
Block a user