Add spine and dialogic #1

Open
m.rossi wants to merge 10 commits from spine into main
14 changed files with 308 additions and 0 deletions
Showing only changes of commit 00cc70df12 - Show all commits

View File

@@ -20,6 +20,13 @@ func _ready() -> void:
# Connect to variable changes to watch for demo variables # Connect to variable changes to watch for demo variables
Dialogic.VAR.variable_changed.connect(_on_variable_changed) Dialogic.VAR.variable_changed.connect(_on_variable_changed)
# Check if we're returning from the gym09 minigame
if Dialogic.VAR.Gym.minigame_done:
print("[Gym Controller] Returning from minigame — starting congrats timeline.")
Dialogic.VAR.Gym.minigame_done = false
Dialogic.start("gym09_congrats")
return
# Start the hub timeline # Start the hub timeline
Dialogic.start(start_timeline) Dialogic.start(start_timeline)
@@ -49,6 +56,14 @@ func _on_dialogic_signal(argument: Variant) -> void:
Dialogic.end_timeline() Dialogic.end_timeline()
Dialogic.start_timeline("gym_pattern4_signal_target") Dialogic.start_timeline("gym_pattern4_signal_target")
elif argument is String and argument == "start_minigame":
# Gym 09: Scene switching demo
# Store the return path so the minigame knows where to come back to
Dialogic.VAR.Gym.minigame_return = "hub"
print("[Gym Controller] Signal 'start_minigame' — switching to minigame scene.")
await get_tree().create_timer(0.3).timeout
get_tree().change_scene_to_file("res://docs/gyms/gym09_scene_switching/gym09_minigame.tscn")
#endregion #endregion

View File

@@ -50,7 +50,24 @@ Miko (neutral): Which pattern would you like to explore?
Miko (joy): The variable itself is the jump target! Miko (joy): The variable itself is the jump target!
jump gym_pattern8_dynamic_label/start jump gym_pattern8_dynamic_label/start
- More patterns...
Miko (smile): There's more to explore on the next page!
jump menu_p2
- I've seen enough. Goodbye! - I've seen enough. Goodbye!
Miko (smile): Thanks for exploring! Come back anytime. Miko (smile): Thanks for exploring! Come back anytime.
Advisor (pl5): Until next time. Advisor (pl5): Until next time.
[end_timeline] [end_timeline]
label menu_p2
Miko (neutral): More patterns to explore:
- 9. Scene switching (Dialogic ↔ gameplay)
Miko (smile): This one changes scenes — a timeline, then a minigame, then back!
Advisor (pl5): I'll take you to the intro, which will trigger the minigame.
Miko (neutral): Press the button 5 times to return.
jump gym09_intro/
- Back to first page
jump menu

View File

@@ -0,0 +1,27 @@
# Gym 09 — Scene Switching
Demonstrates switching between a Dialogic timeline scene and a non-Dialogic gameplay scene.
## Flow
```
gym09_main.tscn gym09_minigame.tscn
┌─────────────────┐ ┌──────────────────┐
│ gym09_intro.dtl │ │ Press button 5x │
│ ↓ │ │ ↓ │
│ [signal] │ ───→ │ set variable │
│ │ ←─── │ change_scene back │
│ gym09_congrats │ │ │
│ .dtl │ └──────────────────┘
└─────────────────┘
```
## How to Run
Open `gym09_main.tscn` in the Godot editor and press **F6** (Run Current Scene).
## Key Mechanic
- `Dialogic.VAR.Gym.minigame_done` acts as a flag between scenes
- `[signal arg=start_minigame]` triggers `get_tree().change_scene_to_file()`
- The controller's `_ready()` checks the flag to know which timeline to start

View File

@@ -0,0 +1,51 @@
# ============================================================
# Gym 09 — Scene Switching: Congratulations Timeline
# ============================================================
# This timeline runs AFTER the player returns from the
# minigame. It congratulates them and explains the mechanism.
# ============================================================
join Miko center
join Advisor right
Miko (joy): You did it! You pressed the button 5 times!
[wait time="0.5"]
Advisor (pl5): Welcome back to a Dialogic timeline.
Advisor (pl5): Notice what just happened:
[wait time="1.0"]
Advisor (pl5): 1. You started in a Dialogic timeline (the intro).
Advisor (pl5): 2. A signal triggered a scene change to a pure\
gameplay scene with no Dialogic UI.
Advisor (pl5): 3. The minigame set a Dialogic variable:\
`Gym.minigame_done = true`
Advisor (pl5): 4. Then it called `change_scene_to_file()` back here.
Advisor (pl5): 5. This scene's controller checked the variable\
and started THIS timeline.
[wait time="2.0"]
Miko (smile): The magic is that Dialogic is an AUTOLOAD!
Miko (joy): Its variables survive scene changes because the\
autoload never gets freed.
Advisor (doubt): This pattern is essential for:
Advisor (doubt): • Minigames (fishing, crafting, lockpicking)
Advisor (doubt): • Combat encounters between dialog scenes
Advisor (doubt): • Exploration that triggers narrative beats
Advisor (doubt): • Any gameplay/dialog hybrid
[wait time="2.0"]
Miko (smile): Want to try again?
- Yes, play the minigame again!
set {Gym.minigame_done} = false
[signal arg=start_minigame]
- No, I understand the pattern. Back to hub.
set {Gym.minigame_done} = false
jump gym_main/menu

View File

@@ -0,0 +1 @@
uid://b8hj1owk0somk

View File

@@ -0,0 +1,37 @@
# ============================================================
# Gym 09 — Scene Switching Controller
# ============================================================
# This root node handles:
# 1. Starting the right timeline on scene load
# (intro on first visit, congrats after minigame)
# 2. Catching the [signal arg=start_minigame] to change scenes
# ============================================================
extends Node2D
const MINIGAME_SCENE_PATH := "res://docs/gyms/gym09_scene_switching/gym09_minigame.tscn"
@export var intro_timeline: DialogicTimeline
@export var congrats_timeline: DialogicTimeline
func _ready() -> void:
# Connect to signal events from timelines
Dialogic.signal_event.connect(_on_dialogic_signal)
# Check if we're returning from the minigame
if Dialogic.VAR.Gym.minigame_done:
print("[Gym09 Controller] Returning from minigame — starting congrats timeline.")
Dialogic.VAR.Gym.minigame_done = false
Dialogic.start(congrats_timeline)
else:
print("[Gym09 Controller] First visit — starting intro timeline.")
Dialogic.start(intro_timeline)
func _on_dialogic_signal(argument: Variant) -> void:
if argument is String and argument == "start_minigame":
print("[Gym09 Controller] Signal 'start_minigame' received — switching to minigame scene.")
# Tell the minigame where to return
Dialogic.VAR.Gym.minigame_return = "standalone"
await get_tree().create_timer(0.3).timeout
get_tree().change_scene_to_file(MINIGAME_SCENE_PATH)

View File

@@ -0,0 +1 @@
uid://k82wjnwinxwp

View File

@@ -0,0 +1,43 @@
# ============================================================
# Gym 09 — Scene Switching: Intro Timeline
# ============================================================
# This timeline introduces the scene-switching concept,
# then emits a signal that triggers a scene change to the
# minigame (a non-Dialogic scene).
# ============================================================
join Miko center
join Advisor right
Miko (joy): Welcome to Gym 09 — Scene Switching!
Advisor (pl5): This gym demonstrates how Dialogic timelines and\
non-Dialogic gameplay scenes can flow back and forth.
[wait time="1.0"]
Miko (neutral): Here's the plan:
Miko (smile): 1. You're reading this dialog (Dialogic timeline).
Advisor (pl5): 2. I'll emit a signal that changes the scene to a\
pure minigame — no dialog, no portraits, just gameplay.
Miko (smile): 3. In the minigame, press the button 5 times!
Advisor (pl5): 4. After 5 presses, you'll automatically return here\
and I'll congratulate you with another timeline.
[wait time="1.5"]
Miko (joy): That shows the full loop:\
Dialogic scene → gameplay scene → Dialogic scene!
Advisor (pl5): The key insight: Dialogic is an autoload, so\
variables persist even in non-Dialogic scenes.
[wait time="1.0"]
Miko (smile): Ready? Let's go to the minigame!
# This signal is caught by gym09_controller.gd,
# which calls get_tree().change_scene_to_file("...minigame.tscn")
[signal arg=start_minigame]
# This line won't be reached — the scene changes immediately
Miko (surprise): Wait, shouldn't I still be here?

View File

@@ -0,0 +1 @@
uid://my3sk1mu2lx6

View File

@@ -0,0 +1,10 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://docs/gyms/gym09_scene_switching/gym09_controller.gd" id="1_ctrl"]
[ext_resource type="Resource" path="res://docs/gyms/gym09_scene_switching/gym09_intro.dtl" id="2_intro"]
[ext_resource type="Resource" path="res://docs/gyms/gym09_scene_switching/gym09_congrats.dtl" id="3_congrats"]
[node name="Gym09SceneSwitching" type="Node2D"]
script = ExtResource("1_ctrl")
intro_timeline = ExtResource("2_intro")
congrats_timeline = ExtResource("3_congrats")

View File

@@ -0,0 +1,55 @@
# ============================================================
# Gym 09 — Minigame Scene
# ============================================================
# A pure gameplay scene with NO Dialogic UI.
# Press the button 5 times, then return to the Dialogic scene.
# ============================================================
extends Control
@export var required_presses: int = 5
var _press_count: int = 0
func _ready() -> void:
print("[Gym09 Minigame] Ready! Press the button ", required_presses, " times.")
_update_label()
func _on_button_pressed() -> void:
_press_count += 1
print("[Gym09 Minigame] Press ", _press_count, " of ", required_presses)
_update_label()
if _press_count >= required_presses:
_on_complete()
func _update_label() -> void:
var remaining := required_presses - _press_count
if remaining > 0:
%CounterLabel.text = "Presses remaining: " + str(remaining)
%Button.text = "Press Me! (" + str(_press_count) + "/" + str(required_presses) + ")"
else:
%CounterLabel.text = "Complete!"
%Button.text = "Returning..."
func _on_complete() -> void:
print("[Gym09 Minigame] Complete! Setting variable and switching back.")
# Dialogic is an autoload, so this works even without a Dialogic layout
Dialogic.VAR.Gym.minigame_done = true
# Disable the button so player can't spam it
%Button.disabled = true
# Brief pause so the player sees "Complete!"
await get_tree().create_timer(1.0).timeout
# Determine where to return based on who started us
var return_path := "res://docs/gyms/gym09_scene_switching/gym09_main.tscn"
if Dialogic.VAR.Gym.minigame_return == "hub":
return_path = "res://docs/gyms/cross-timeline-interactions/gym_controller.tscn"
print("[Gym09 Minigame] Returning to: ", return_path)
get_tree().change_scene_to_file(return_path)

View File

@@ -0,0 +1 @@
uid://bpu7ehr2qjc1f

View File

@@ -0,0 +1,45 @@
[gd_scene format=3 uid="uid://p83ndnog4l2h"]
[ext_resource type="Script" uid="uid://bpu7ehr2qjc1f" path="res://docs/gyms/gym09_scene_switching/gym09_minigame.gd" id="1_script"]
[node name="Gym09Minigame" type="Control" unique_id=313626257]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_script")
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=419254278]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -150.0
offset_top = -80.0
offset_right = 150.0
offset_bottom = 80.0
grow_horizontal = 2
grow_vertical = 2
[node name="TitleLabel" type="Label" parent="VBoxContainer" unique_id=529708548]
layout_mode = 2
text = "MINIGAME"
horizontal_alignment = 1
[node name="CounterLabel" type="Label" parent="VBoxContainer" unique_id=1005168486]
unique_name_in_owner = true
layout_mode = 2
text = "Presses remaining: 5"
horizontal_alignment = 1
[node name="Button" type="Button" parent="VBoxContainer" unique_id=211036225]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "Press Me! (0/5)"
[connection signal="pressed" from="VBoxContainer/Button" to="." method="_on_button_pressed"]

View File

@@ -27,6 +27,8 @@ directories/dch_directory={
"Raptor": "res://docs/museums/dialogic/Raptor.dch" "Raptor": "res://docs/museums/dialogic/Raptor.dch"
} }
directories/dtl_directory={ directories/dtl_directory={
"gym09_congrats": "res://docs/gyms/gym09_scene_switching/gym09_congrats.dtl",
"gym09_intro": "res://docs/gyms/gym09_scene_switching/gym09_intro.dtl",
"gym_main": "res://docs/gyms/cross-timeline-interactions/gym_main.dtl", "gym_main": "res://docs/gyms/cross-timeline-interactions/gym_main.dtl",
"gym_pattern1_jump": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_jump.dtl", "gym_pattern1_jump": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_jump.dtl",
"gym_pattern1_target": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_target.dtl", "gym_pattern1_target": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_target.dtl",
@@ -50,6 +52,8 @@ layout/default_style="Default"
variables={ variables={
"Gym": { "Gym": {
"demo_flag": false, "demo_flag": false,
"minigame_done": false,
"minigame_return": "",
"player_name": "Hero", "player_name": "Hero",
"sub_value": 0, "sub_value": 0,
"time_of_day": "morning" "time_of_day": "morning"