Add cross timeline interaction gym
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ extends DialogicPortrait
|
||||
func _ready() -> void:
|
||||
$SpineSprite.animation_completed.connect(_on_animation_completed)
|
||||
|
||||
# Just a signal connection example
|
||||
func _on_animation_completed(spine_sprite: Object, animation_state: SpineAnimationState, track_entry: SpineTrackEntry) -> void:
|
||||
print(track_entry)
|
||||
pass
|
||||
|
||||
487
docs/gyms/cross-timeline-interactions/README.md
Normal file
487
docs/gyms/cross-timeline-interactions/README.md
Normal file
@@ -0,0 +1,487 @@
|
||||
# Cross-Timeline Interactions Gym
|
||||
|
||||
## Overview
|
||||
|
||||
Dialogic 2 provides **seven distinct mechanisms** for multiple timelines to interact
|
||||
with each other. This gym catalogs and demonstrates every mechanism, so you can choose
|
||||
the right one for your narrative structure.
|
||||
|
||||
---
|
||||
|
||||
## The Seven Mechanisms
|
||||
|
||||
| # | Mechanism | Timeline Syntax | GDScript Required? | Preserves Jump Stack? |
|
||||
|---|-----------|-----------------|-------------------|----------------------|
|
||||
| 1 | **Jump to another timeline** | `jump TimelineName/Label` | No | Yes (push) |
|
||||
| 2 | **Jump + Return (subroutine)** | `jump` → … → `return` | No | Yes (push/pop) |
|
||||
| 3 | **Choice → different timeline** | `- Option` → `jump TimelineB/` | No | Depends |
|
||||
| 4 | **Condition → different timeline** | `if {var}:` → `jump TimelineB/` | No | Depends |
|
||||
| 5 | **Variable set → condition in next timeline** | `set {x} = 1` → `if {x}:` in other timeline | No (but needs trigger) | N/A |
|
||||
| 6 | **Signal event → code → `start_timeline()`** | `[signal arg=...]` | **Yes** | No (manual) |
|
||||
| 7 | **`do` Call event → autoload method → `start_timeline()`** | `do MyAutoload.method()` | **Yes** | No (manual) |
|
||||
| 8 | **GDScript `timeline_ended` signal → chain** | None | **Yes** (external) | N/A |
|
||||
| 9 | **Text input → variable → subsequent timeline** | `[text_input ...]` → `if {name}:` | No | N/A |
|
||||
| 10 | **Dynamic label jump (`jump {variable}`)** | `jump {day_phase}` | No | Yes |
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 1: Direct Jump Between Timelines
|
||||
|
||||
The simplest form of cross-timeline interaction. A `jump` event in one timeline immediately
|
||||
transitions to another timeline (at a specific label or the beginning).
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# timeline_a.dtl
|
||||
join Miko center
|
||||
Miko (neutral): I need to go somewhere else now.
|
||||
jump timeline_b/arrival_label
|
||||
|
||||
# timeline_b.dtl
|
||||
label arrival_label
|
||||
join Advisor left
|
||||
Advisor (pl5): Welcome! You came from timeline_a.
|
||||
```
|
||||
|
||||
### What Happens
|
||||
- The current timeline + event index is **pushed** onto the jump stack
|
||||
- Dialogic loads `timeline_b` and starts at `arrival_label` (or the beginning if no label)
|
||||
- `Dialogic.Jump.switched_timeline` and `Dialogic.Jump.jumped_to_label` signals fire
|
||||
- The old timeline is NOT destroyed — it lives on the jump stack
|
||||
|
||||
### Key Property
|
||||
**Jump to label in another timeline:** `jump TimelineName/LabelIdentifier`
|
||||
**Jump to beginning of another timeline:** `jump TimelineName/`
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 2: Jump + Return (Subroutine Pattern)
|
||||
|
||||
A timeline acts as a reusable "subroutine" — it does some work (sets variables, plays a scene,
|
||||
shows a choice), then `return` sends execution back to where the jump occurred.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# main_story.dtl
|
||||
join Miko center
|
||||
Miko (joy): Let's check our inventory first.
|
||||
jump inventory_check/ # Push current position onto stack
|
||||
Miko (smile): Back from inventory. Let's continue.
|
||||
|
||||
# inventory_check.dtl
|
||||
Advisor (pl5): Opening inventory...
|
||||
set {coins} += 50
|
||||
Advisor (pl5): You found 50 coins!
|
||||
return # Pop stack → resume main_story right after the jump
|
||||
```
|
||||
|
||||
### Stack Behavior
|
||||
```
|
||||
main_story stack: []
|
||||
jump inventory stack: [(main_story, idx_2)]
|
||||
── inventory_check runs ──
|
||||
return stack: [] (popped)
|
||||
resumes at main_story idx_3
|
||||
```
|
||||
|
||||
### Key Property
|
||||
- If the stack is **empty** when `return` fires, the timeline ends
|
||||
- You can jump to one sub-timeline which jumps to another, creating a deep call stack
|
||||
- This is ideal for: inventory screens, crafting menus, mini-dialogues, flashbacks
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 3: Choice → Different Timeline
|
||||
|
||||
A dialogue choice doesn't just set a variable — it directly `jump`s to an entirely different
|
||||
timeline for each option.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# crossroad.dtl (hub)
|
||||
join Miko center
|
||||
Advisor (doubt): Where should we go, Miko?
|
||||
- The village market
|
||||
Miko (joy): Let's go shopping!
|
||||
jump market_scene/start
|
||||
|
||||
- The dark forest
|
||||
Miko (shock): Into the woods...
|
||||
jump forest_scene/start
|
||||
|
||||
- Stay here and rest
|
||||
Miko (smile): Good idea. Let's camp.
|
||||
jump camp_scene/start
|
||||
```
|
||||
|
||||
### What Happens
|
||||
- Player chooses "The village market"
|
||||
- Events inside that branch execute (text, variables, signals)
|
||||
- The `jump marketplace_scene/start` fires
|
||||
- Execution moves to `market_scene.dtl` at the `start` label
|
||||
|
||||
### Key Property
|
||||
Each choice branch can have **multiple events** before the jump — set variables,
|
||||
show reactions, play sounds — making it a full pre-transition sequence.
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 4: Condition → Different Timeline
|
||||
|
||||
Instead of a player choice, automatic branching based on variable values can route
|
||||
to different timelines.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# auto_router.dtl
|
||||
if {coins} >= 100:
|
||||
Miko (joy): I'm rich! Let's go to the VIP area.
|
||||
jump vip_timeline/
|
||||
elif {reputation} <= -10:
|
||||
Miko (anger): They hate me here. I should leave town.
|
||||
jump exile_timeline/
|
||||
else:
|
||||
Miko (neutral): Just an ordinary day.
|
||||
jump normal_day/
|
||||
```
|
||||
|
||||
### What Happens
|
||||
- Variables `coins` and `reputation` were set by **previous timelines**
|
||||
- When `auto_router` starts, Dialogic evaluates each condition
|
||||
- The matching branch executes and jumps to the appropriate timeline
|
||||
- If no condition matches, `else` runs (or nothing happens if no `else`)
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 5: Variable Set in One Timeline, Checked in Another
|
||||
|
||||
The most loosely-coupled pattern. Timeline A sets a variable. Later, Timeline B
|
||||
checks it with a `Condition` event. They don't jump to each other directly but
|
||||
something external (code, another timeline, signal) starts Timeline B.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# quest_start.dtl
|
||||
Miko (surprise): A dragon?! I accept the quest!
|
||||
set {dragon_quest_accepted} = true
|
||||
set {quest_difficulty} = "hard"
|
||||
[end_timeline]
|
||||
|
||||
# town_square.dtl (started later by code)
|
||||
if {dragon_quest_accepted}:
|
||||
Merchant (happy): Heard you're hunting a dragon. Need supplies?
|
||||
set {coins} -= 50
|
||||
Merchant (happy): Here's a fireproof shield!
|
||||
else:
|
||||
Merchant (neutral): Just browsing today?
|
||||
```
|
||||
|
||||
### Key Property
|
||||
- Zero timeline coupling — `town_square.dtl` doesn't know or care **which** timeline
|
||||
set `{dragon_quest_accepted}`
|
||||
- Variables act as a shared "world state" between all timelines
|
||||
- Variable changes persist across jumps, returns, and `start_timeline()` calls
|
||||
- To reset: `Dialogic.VAR.reset()` or explicitly `set {quest_accepted} = false`
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 6: Signal Event → GDScript → `start_timeline()`
|
||||
|
||||
Timeline A emits a signal. GDScript code listens for it and starts Timeline B.
|
||||
This is the most flexible pattern — the code can do anything before starting the next
|
||||
timeline (load resources, change scenes, animate the world, etc.).
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# scene_cutscene.dtl
|
||||
join Raptor (walk) center
|
||||
Raptor (walk): Watch this transformation!
|
||||
[signal arg=start_transformation]
|
||||
[wait time="2.0"]
|
||||
Raptor (roar): ROAR!
|
||||
[end_timeline]
|
||||
```
|
||||
|
||||
### GDScript
|
||||
|
||||
```gdscript
|
||||
# cross_timeline_gym.gd
|
||||
extends Node2D
|
||||
|
||||
func _ready():
|
||||
Dialogic.signal_event.connect(_on_dialogic_signal)
|
||||
|
||||
func _on_dialogic_signal(argument: String):
|
||||
if argument == "start_transformation":
|
||||
# Do anything here: animate the world, change scenes, play music
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
Dialogic.start_timeline("transformation_sequence")
|
||||
```
|
||||
|
||||
### Signal Data Options
|
||||
- **String signal:** `[signal arg="activate_portal"]`
|
||||
- **Dictionary signal:** `[signal arg_type="Dictionary" arg={"target": "forest", "time": "night"}]`
|
||||
|
||||
### Key Property
|
||||
The signal event does **not** end the current timeline — both timelines can run if you don't
|
||||
stop the first one. Use `Dialogic.end_timeline()` to explicitly end before starting a new one.
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 7: `do` Call Event → Autoload Method
|
||||
|
||||
Similar to signal but with **typed arguments** and **method return values**.
|
||||
The timeline calls a method on any autoload singleton, which can start new timelines.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# trigger.dtl
|
||||
Miko (neutral): Let me check something...
|
||||
do GameManager.start_side_quest("forest_rescue", "hard")
|
||||
```
|
||||
|
||||
### Autoload (GameManager.gd)
|
||||
|
||||
```gdscript
|
||||
extends Node
|
||||
|
||||
func start_side_quest(quest_name: String, difficulty: String):
|
||||
print("Starting quest: ", quest_name, " at ", difficulty)
|
||||
Dialogic.start_timeline(quest_name)
|
||||
```
|
||||
|
||||
### Key Difference from Signal
|
||||
- `do` calls a **specific method** with typed arguments (int, string, bool, array, expression)
|
||||
- `[signal]` emits a signal with a single untyped argument (string or dictionary)
|
||||
- `do` is better for well-defined APIs; `[signal]` is better for loose coupling
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 8: GDScript `timeline_ended` Chaining
|
||||
|
||||
Completely external to the timeline itself. Code listens for when Timeline A finishes,
|
||||
then immediately starts Timeline B.
|
||||
|
||||
### GDScript
|
||||
|
||||
```gdscript
|
||||
extends Node2D
|
||||
|
||||
@export var timeline_sequence: Array[DialogicTimeline] = []
|
||||
var current_timeline_index := 0
|
||||
|
||||
func _ready():
|
||||
Dialogic.timeline_ended.connect(_on_timeline_ended)
|
||||
Dialogic.start(timeline_sequence[0])
|
||||
|
||||
func _on_timeline_ended():
|
||||
current_timeline_index += 1
|
||||
if current_timeline_index < timeline_sequence.size():
|
||||
await get_tree().create_timer(1.0).timeout # optional pause
|
||||
Dialogic.start(timeline_sequence[current_timeline_index])
|
||||
```
|
||||
|
||||
### Key Property
|
||||
- The timelines themselves don't know about each other
|
||||
- You can insert any logic between timelines (fades, scene loads, world changes)
|
||||
- Order is determined by the exported array, editable in the Godot inspector
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 9: Text Input → Variable → Next Timeline
|
||||
|
||||
The player types text that gets stored in a variable. A subsequent timeline
|
||||
(or condition block) uses that variable to route or personalize the dialog.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# name_entry.dtl
|
||||
Miko (smile): Welcome, traveler!
|
||||
Advisor (pl5): Before we begin... what should I call you?
|
||||
[text_input text="Your name?" var="player_name" placeholder="Enter name..." default="Hero" allow_empty="false"]
|
||||
Miko (joy): Nice to meet you, {player_name}!
|
||||
jump personalized_greeting/
|
||||
|
||||
# personalized_greeting.dtl
|
||||
if {player_name} == "Miko":
|
||||
Miko (surprise): Hey, that's MY name!
|
||||
elif {player_name} == "Dragon":
|
||||
Miko (shock): Um... should I be worried?
|
||||
else:
|
||||
Miko (smile): {player_name}, what a wonderful name!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mechanism 10: Dynamic Label Jump (`jump {variable}`)
|
||||
|
||||
Use a variable's value as the jump target. This is powerful for state-machine-like
|
||||
narrative flow.
|
||||
|
||||
### Timeline Text
|
||||
|
||||
```
|
||||
# daily_cycle.dtl
|
||||
# day_phase is set elsewhere: "morning", "afternoon", "evening", "night"
|
||||
jump {day_phase}
|
||||
|
||||
label morning
|
||||
Miko (joy): Good morning! The sun is rising.
|
||||
jump end_of_day
|
||||
|
||||
label afternoon
|
||||
Miko (neutral): It's a warm afternoon.
|
||||
jump end_of_day
|
||||
|
||||
label evening
|
||||
Miko (shock): The sun is setting. We should hurry!
|
||||
jump end_of_day
|
||||
|
||||
label night
|
||||
Miko (doubt): It's dark... be careful out there.
|
||||
jump end_of_day
|
||||
|
||||
label end_of_day
|
||||
[end_timeline]
|
||||
```
|
||||
|
||||
### Key Property
|
||||
- The variable must resolve to a label identifier that exists in the current timeline
|
||||
- Can also reference timeline: `jump {target_timeline}/{target_label}`
|
||||
|
||||
---
|
||||
|
||||
## Combining Patterns: A Practical Example
|
||||
|
||||
Real games combine multiple mechanisms. Here's a complex example:
|
||||
|
||||
```
|
||||
# hub_world.dtl (entry point)
|
||||
label hub
|
||||
Miko (neutral): What should I do?
|
||||
- Visit the shop → jump shop_dialogue/
|
||||
- Check quest board → jump quest_board/
|
||||
- Talk to NPCs → if {met_elder}: jump elder_chat/
|
||||
else: jump elder_intro/
|
||||
- Save and quit → [save slot="auto"]
|
||||
[end_timeline]
|
||||
```
|
||||
|
||||
```
|
||||
# quest_board.dtl
|
||||
label start
|
||||
set {viewed_board} = true
|
||||
QuestGiver (happy): Here are today's quests!
|
||||
- Hunt 5 wolves (reward: 50g)
|
||||
set {active_quest} = "wolf_hunt"
|
||||
[signal arg=quest_accepted]
|
||||
return # ← back to hub
|
||||
|
||||
- Gather 10 herbs (reward: 30g)
|
||||
set {active_quest} = "herb_gathering"
|
||||
[signal arg=quest_accepted]
|
||||
return
|
||||
|
||||
- Maybe later
|
||||
return
|
||||
```
|
||||
|
||||
The `[signal arg=quest_accepted]` is caught by GDScript which updates the world
|
||||
(spawns wolves, shows quest marker) and then call `jump hub_world/hub` to return the
|
||||
player to the hub.
|
||||
|
||||
---
|
||||
|
||||
## Decision Guide: Which Mechanism to Use?
|
||||
|
||||
| Use case | Best mechanism |
|
||||
|----------|---------------|
|
||||
| Narrative branches that diverge forever | **Jump to another timeline** (1) |
|
||||
| Reusable mini-scenes (inventory, crafting, shop) | **Jump + Return** (2) |
|
||||
| Player choice determines next scene | **Choice → Jump to timeline** (3) |
|
||||
| Automatic scene routing based on game state | **Condition → Jump** (4) or **Dynamic label jump** (10) |
|
||||
| World state affects dialog in another scene | **Variable set in one, checked in another** (5) |
|
||||
| Cutscene triggers world/engine changes | **Signal → GDScript** (6) or **do Call** (7) |
|
||||
| Linear sequence of cutscenes | **`timeline_ended` chaining** (8) |
|
||||
| Player-customized content | **Text input → variable** (9) |
|
||||
| Time-of-day / game phase routing | **Dynamic label jump** (10) |
|
||||
|
||||
---
|
||||
|
||||
## Project Files in This Gym
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `gym_main.dtl` | Hub timeline — presents a menu of all patterns to explore |
|
||||
| `gym_pattern1_jump.dtl` | Mechanism 1: Simple cross-timeline jump |
|
||||
| `gym_pattern1_target.dtl` | Target timeline for pattern 1 |
|
||||
| `gym_pattern2_subroutine.dtl` | Mechanism 2: Jump → work → return |
|
||||
| `gym_pattern2_caller.dtl` | Calls the subroutine |
|
||||
| `gym_pattern3_variable_chain_A.dtl` | Mechanism 5: Sets a variable |
|
||||
| `gym_pattern3_variable_chain_B.dtl` | Mechanism 5: Checks the variable |
|
||||
| `gym_pattern4_signal_chain.dtl` | Mechanism 6: Emits a signal |
|
||||
| `gym_pattern4_signal_target.dtl` | Mechanism 6: Started by signal handler |
|
||||
| `gym_pattern5_choice_jump.dtl` | Mechanism 3: Choice routes to different timelines |
|
||||
| `gym_pattern6_condition_router.dtl` | Mechanism 4: Variable conditions route timelines |
|
||||
| `gym_pattern7_text_input.dtl` | Mechanism 9: Player input drives next timeline |
|
||||
| `gym_pattern8_dynamic_label.dtl` | Mechanism 10: Variable-driven label jump |
|
||||
| `gym_pattern8_morning.dtl` | Day phase: morning |
|
||||
| `gym_pattern8_night.dtl` | Day phase: night |
|
||||
| `gym_controller.gd` | GDScript controller with signal handlers |
|
||||
|
||||
---
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Create the scene in Godot editor
|
||||
|
||||
The `.dtl` and `.gd` files are already in place. You just need to create the scene:
|
||||
|
||||
1. Open the Godot editor for this project
|
||||
2. Create a new scene: **Scene → New Scene**
|
||||
3. Set the root node to **Node2D** and name it `CrossTimelineGym`
|
||||
4. Attach the script: click the script icon, choose `res://docs/gyms/cross-timeline-interactions/gym_controller.gd`
|
||||
5. In the Inspector, assign `start_timeline` to `res://docs/gyms/cross-timeline-interactions/gym_main.dtl`
|
||||
6. Save as `res://docs/gyms/cross-timeline-interactions/gym_controller.tscn`
|
||||
|
||||
### 2. Verify project settings
|
||||
|
||||
The `project.godot` file has already been updated with:
|
||||
- All 14 gym timelines registered in `directories/dtl_directory`
|
||||
- New variables: `Gym.demo_flag`, `Gym.sub_value`, `Gym.player_name`, `Gym.time_of_day`, `Museum.coins`, `Museum.met_merchant`, `Museum.player_has_key`
|
||||
|
||||
### 3. Run the scene
|
||||
|
||||
Press **F6** (Run Current Scene) with `gym_controller.tscn` open to start the gym.
|
||||
|
||||
### Running individual patterns
|
||||
|
||||
To test a specific pattern without the hub, call from any GDScript:
|
||||
```gdscript
|
||||
Dialogic.start("gym_pattern1_jump") # Direct jump demo
|
||||
Dialogic.start("gym_pattern2_caller", "start") # Subroutine demo
|
||||
Dialogic.start("gym_pattern5_choice_jump", "start") # Choice routing demo
|
||||
Dialogic.start("gym_pattern8_dynamic_label", "start") # Dynamic label demo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limitations & Caveats
|
||||
|
||||
1. **Only one timeline runs at a time.** You cannot have two parallel timelines executing simultaneously. Dialogic is single-timeline.
|
||||
|
||||
2. **Timeline references must be preregistered.** The target timeline must have its `.dtl` file listed in `project.godot` → `dialogic/directories/dtl_directory` before `jump TimelineName/` will work.
|
||||
|
||||
3. **Jump stack is hidden state.** If you `jump` without `return`, the stack grows without bound. Use `Dialogic.Jump.is_jump_stack_empty()` to check.
|
||||
|
||||
4. **`start_timeline()` bypasses the jump stack.** Unlike `jump`, calling `Dialogic.start_timeline()` replaces the current timeline without pushing to the stack. Use `jump` in timelines, `start_timeline()` in code.
|
||||
|
||||
5. **`Dialogic.start()` vs `Dialogic.start_timeline()`.** The former also loads a layout scene (use for the first dialog in a scene). The latter only starts the timeline (use when a layout is already active).
|
||||
108
docs/gyms/cross-timeline-interactions/gym_controller.gd
Normal file
108
docs/gyms/cross-timeline-interactions/gym_controller.gd
Normal file
@@ -0,0 +1,108 @@
|
||||
# ============================================================
|
||||
# Cross-Timeline Interactions Gym — Controller
|
||||
# ============================================================
|
||||
# This script demonstrates GDScript-driven cross-timeline
|
||||
# interactions: signal handling, timeline chaining, and
|
||||
# dynamic timeline starting.
|
||||
# ============================================================
|
||||
extends Node2D
|
||||
|
||||
@export var start_timeline: DialogicTimeline
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Connect to Dialogic's signal_event to react to [signal arg=...]
|
||||
Dialogic.signal_event.connect(_on_dialogic_signal)
|
||||
|
||||
# Connect to timeline_ended for chaining patterns
|
||||
Dialogic.timeline_ended.connect(_on_timeline_ended)
|
||||
|
||||
# Connect to variable changes to watch for demo variables
|
||||
Dialogic.VAR.variable_changed.connect(_on_variable_changed)
|
||||
|
||||
# Start the hub timeline
|
||||
Dialogic.start(start_timeline)
|
||||
|
||||
print("[Gym Controller] Ready. Listening for signals and timeline events.")
|
||||
|
||||
|
||||
#region SIGNAL HANDLING (Pattern 4)
|
||||
|
||||
# Called whenever a [signal arg=...] event fires in any timeline
|
||||
func _on_dialogic_signal(argument: Variant) -> void:
|
||||
print("[Gym Controller] Signal received: ", argument)
|
||||
|
||||
if argument is String and argument == "demo_chain":
|
||||
# Pattern 4: Signal → GDScript → New Timeline
|
||||
# The signal timeline continues running, but we start a new one here.
|
||||
# In a real game, you might call end_timeline() first:
|
||||
# Dialogic.end_timeline()
|
||||
# For this demo, we let gym_pattern4_signal_chain finish naturally
|
||||
# and then start the target timeline when timeline_ended fires.
|
||||
print("[Gym Controller] 'demo_chain' signal detected. Will start target timeline after current one ends.")
|
||||
# We store the intent and act on timeline_ended
|
||||
_pending_signal_target = "gym_pattern4_signal_target"
|
||||
|
||||
elif argument is String and argument == "start_from_code":
|
||||
# Example: start a specific timeline on demand
|
||||
print("[Gym Controller] Starting timeline from code signal.")
|
||||
Dialogic.end_timeline()
|
||||
Dialogic.start_timeline("gym_pattern4_signal_target")
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region TIMELINE CHAINING (Pattern 8 extension)
|
||||
|
||||
var _pending_signal_target: String = ""
|
||||
|
||||
|
||||
func _on_timeline_ended() -> void:
|
||||
print("[Gym Controller] Timeline ended.")
|
||||
|
||||
# Pattern 4: If we had a pending signal target, start it now
|
||||
if not _pending_signal_target.is_empty():
|
||||
var target := _pending_signal_target
|
||||
_pending_signal_target = ""
|
||||
print("[Gym Controller] Starting pending target timeline: ", target)
|
||||
# Small delay so the player can see the transition
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
Dialogic.start_timeline(target)
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region VARIABLE WATCHING (for debugging/demo)
|
||||
|
||||
func _on_variable_changed(info: Dictionary) -> void:
|
||||
# Only log our demo variables to avoid noise
|
||||
var demo_vars := ["Gym.demo_flag", "Gym.sub_value", "Gym.player_name",
|
||||
"Gym.time_of_day", "Museum.coins", "Museum.met_merchant", "Museum.player_has_key"]
|
||||
if info.variable in demo_vars:
|
||||
print("[Gym Controller] Variable changed: ", info.variable, " = ", info.new_value)
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region UTILITY: Start a specific pattern from code
|
||||
|
||||
# You can call these from the Godot editor's remote scene tree
|
||||
# or from other autoloads using: GymController.start_pattern_1()
|
||||
|
||||
func start_pattern_1() -> void:
|
||||
Dialogic.end_timeline()
|
||||
Dialogic.start_timeline("gym_pattern1_jump")
|
||||
|
||||
func start_pattern_2() -> void:
|
||||
Dialogic.end_timeline()
|
||||
Dialogic.start_timeline("gym_pattern2_caller", "start")
|
||||
|
||||
func start_pattern_5() -> void:
|
||||
Dialogic.end_timeline()
|
||||
Dialogic.start_timeline("gym_pattern5_choice_jump", "start")
|
||||
|
||||
func return_to_hub() -> void:
|
||||
Dialogic.end_timeline()
|
||||
Dialogic.start_timeline("gym_main", "menu")
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://rc4w42wk3axw
|
||||
56
docs/gyms/cross-timeline-interactions/gym_main.dtl
Normal file
56
docs/gyms/cross-timeline-interactions/gym_main.dtl
Normal file
@@ -0,0 +1,56 @@
|
||||
# ============================================================
|
||||
# Cross-Timeline Interactions Gym — Hub
|
||||
# ============================================================
|
||||
# Starting point for exploring all cross-timeline patterns.
|
||||
# Pick a pattern to see it in action, then return here.
|
||||
# ============================================================
|
||||
|
||||
join Miko center
|
||||
join Advisor right
|
||||
|
||||
Advisor (pl5): Welcome to the Cross-Timeline Interactions Gym!
|
||||
Miko (joy): Here you can explore all the ways Dialogic timelines\
|
||||
can talk to each other.
|
||||
|
||||
label menu
|
||||
|
||||
Miko (neutral): Which pattern would you like to explore?
|
||||
|
||||
- 1. Jump to another timeline
|
||||
Miko (smile): Simple! Let's see a direct jump in action.
|
||||
jump gym_pattern1_jump/
|
||||
|
||||
- 2. Jump + Return (subroutine)
|
||||
Miko (joy): Like calling a function, but with dialog!
|
||||
jump gym_pattern2_caller/start
|
||||
|
||||
- 3. Variable chain (set in A, check in B)
|
||||
Miko (neutral): Let's set some variables and see them carry over.
|
||||
set {Gym.demo_flag} = false
|
||||
jump gym_pattern3_variable_chain_A/start
|
||||
|
||||
- 4. Signal → GDScript → Timeline
|
||||
Miko (smile): Code listens for a signal and starts the next timeline.
|
||||
[wait time="0.5"]
|
||||
jump gym_pattern4_signal_chain/start
|
||||
|
||||
- 5. Choice routes to different timelines
|
||||
Miko (joy): Each choice jumps to a completely different scene!
|
||||
jump gym_pattern5_choice_jump/start
|
||||
|
||||
- 6. Condition-based routing
|
||||
Miko (doubt): Variables decide which timeline runs, not the player.
|
||||
jump gym_pattern6_condition_router/start
|
||||
|
||||
- 7. Player text input drives the next timeline
|
||||
Miko (smile): Type your name and see what happens!
|
||||
jump gym_pattern7_text_input/start
|
||||
|
||||
- 8. Dynamic label jump with variables
|
||||
Miko (joy): The variable itself is the jump target!
|
||||
jump gym_pattern8_dynamic_label/start
|
||||
|
||||
- I've seen enough. Goodbye!
|
||||
Miko (smile): Thanks for exploring! Come back anytime.
|
||||
Advisor (pl5): Until next time.
|
||||
[end_timeline]
|
||||
1
docs/gyms/cross-timeline-interactions/gym_main.dtl.uid
Normal file
1
docs/gyms/cross-timeline-interactions/gym_main.dtl.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cclcyrwol0e7x
|
||||
20
docs/gyms/cross-timeline-interactions/gym_pattern1_jump.dtl
Normal file
20
docs/gyms/cross-timeline-interactions/gym_pattern1_jump.dtl
Normal file
@@ -0,0 +1,20 @@
|
||||
# ============================================================
|
||||
# Pattern 1: Direct Jump to Another Timeline
|
||||
# ============================================================
|
||||
# This timeline demonstrates the simplest cross-timeline
|
||||
# interaction: jumping directly to a different timeline.
|
||||
# ============================================================
|
||||
|
||||
join Miko center
|
||||
|
||||
Miko (neutral): I'm in gym_pattern1_jump.dtl right now.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
Miko (joy): Watch this — I'm jumping to another timeline!
|
||||
|
||||
# This jumps to gym_pattern1_target.dtl at the "arrival" label
|
||||
jump gym_pattern1_target/arrival
|
||||
|
||||
# This line is never reached unless someone returns here
|
||||
Miko (surprise): How did I get back here? Nobody called return!
|
||||
@@ -0,0 +1 @@
|
||||
uid://wes5tngbhcr4
|
||||
@@ -0,0 +1,33 @@
|
||||
# ============================================================
|
||||
# Pattern 1 Target: Arrival Timeline
|
||||
# ============================================================
|
||||
# This is the timeline we jumped TO from gym_pattern1_jump.dtl.
|
||||
# It demonstrates that we can land at a specific label.
|
||||
# ============================================================
|
||||
|
||||
label arrival
|
||||
|
||||
join Advisor right
|
||||
|
||||
Advisor (pl5): Welcome to gym_pattern1_target.dtl!
|
||||
Advisor (pl5): You jumped here from gym_p_jump.dtl.
|
||||
Advisor (doubt): Notice we landed at the attern1"arrival" label — not the beginning.
|
||||
|
||||
Miko (surprise): That's right! I specified `jump gym_pattern1_target/arrival`.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
Advisor (pl5): The jump stack has your previous timeline saved.
|
||||
Advisor (pl5): If this timeline ends naturally, there's nothing to return to.
|
||||
|
||||
Miko (smile): Let's go back to the main hub now.
|
||||
Miko (joy): But first, did you notice the jump syntax?
|
||||
|
||||
# Tutorial note
|
||||
Advisor (pl5): The syntax is `jump TimelineName/LabelName`
|
||||
Advisor (doubt): To jump to the beginning, use `jump TimelineName/`
|
||||
|
||||
[wait time="1.5"]
|
||||
|
||||
Miko (neutral): Ready to go back?
|
||||
jump gym_main/menu
|
||||
@@ -0,0 +1 @@
|
||||
uid://lopj8f6gaa2w
|
||||
@@ -0,0 +1,42 @@
|
||||
# ============================================================
|
||||
# Pattern 2: Jump + Return (Subroutine Pattern)
|
||||
# ============================================================
|
||||
# This timeline CALLS a sub-timeline (gym_pattern2_subroutine.dtl)
|
||||
# which does some work, then RETURNS control back here.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
join Advisor right
|
||||
|
||||
Miko (neutral): I'm in the caller timeline.
|
||||
Advisor (pl5): Let me check something in the subroutine...
|
||||
|
||||
# Save current position and jump to the subroutine
|
||||
jump gym_pattern2_subroutine/start
|
||||
|
||||
# Execution resumes HERE after the subroutine calls "return"
|
||||
label after_return
|
||||
|
||||
Miko (joy): Welcome back! The subroutine finished.
|
||||
Advisor (pl5): Notice how we resumed right where we left off.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
# Let's demonstrate: the subroutine set a variable
|
||||
if {Gym.sub_value} == 42:
|
||||
Miko (smile): The subroutine set `Gym.sub_value` to {Gym.sub_value}!
|
||||
Advisor (pl5): Variables set in subroutines persist after return.
|
||||
else:
|
||||
Miko (doubt): Hmm, the subroutine didn't set the value correctly.
|
||||
Advisor (doubt): Something went wrong — `Gym.sub_value` is {Gym.sub_value}.
|
||||
|
||||
[wait time="1.5"]
|
||||
|
||||
Miko (neutral): That's the subroutine pattern in a nutshell!
|
||||
Advisor (pl5): Jump → work → return. Powerful for reusable dialog chunks.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
jump gym_main/menu
|
||||
@@ -0,0 +1 @@
|
||||
uid://ocwrsq7faxi6
|
||||
@@ -0,0 +1,35 @@
|
||||
# ============================================================
|
||||
# Pattern 2 Subroutine: The "Called" Timeline
|
||||
# ============================================================
|
||||
# This is the subroutine — a reusable dialog chunk that does
|
||||
# some work and then returns to wherever it was called from.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
leave Miko
|
||||
leave Advisor
|
||||
|
||||
join Merchant left
|
||||
|
||||
Merchant (happy): I'm the subroutine timeline!
|
||||
Merchant (blink): I can do work independently and then return.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
Merchant (happy): First, let me set a variable...
|
||||
set {Gym.sub_value} = 42
|
||||
|
||||
Merchant (happy): Variable `Gym.sub_value` is now 42.
|
||||
Merchant (blink): The calling timeline will see this change!
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
Merchant (surprise): Now watch this — I'll call `return`...
|
||||
|
||||
# This pops the jump stack and resumes the caller
|
||||
# right after the `jump` that called us
|
||||
return
|
||||
|
||||
# Nothing below here will execute
|
||||
Merchant (happy): You should never see this text!
|
||||
@@ -0,0 +1 @@
|
||||
uid://yyl61w1o1vpp
|
||||
@@ -0,0 +1,24 @@
|
||||
# ============================================================
|
||||
# Pattern 3: Variable Chain (A sets, B checks)
|
||||
# ============================================================
|
||||
# Timeline A sets a variable. Later, Timeline B checks it.
|
||||
# The timelines don't jump to each other — the hub handles routing.
|
||||
# ============================================================
|
||||
|
||||
# Timeline A: Set the variable
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
|
||||
Miko (neutral): I'm in timeline A of the variable chain.
|
||||
Miko (smile): I'm going to set a variable that another timeline will check.
|
||||
|
||||
# Set the flag
|
||||
set {Gym.demo_flag} = true
|
||||
|
||||
Miko (joy): Done! `Gym.demo_flag` is now true.
|
||||
Miko (neutral): The hub will now show me timeline B to see the result.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
jump gym_pattern3_variable_chain_B/start
|
||||
@@ -0,0 +1 @@
|
||||
uid://5b27781x33d8
|
||||
@@ -0,0 +1,30 @@
|
||||
# ============================================================
|
||||
# Pattern 3: Variable Chain — Timeline B (checks the variable)
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Advisor right
|
||||
|
||||
Advisor (pl5): I'm timeline B. Let me check the variable you set...
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
if {Gym.demo_flag}:
|
||||
Advisor (surprise): It's TRUE! Timeline A successfully set it.
|
||||
Advisor (pl5): This proves that variables persist across timeline\
|
||||
boundaries — jumps, returns, even `start_timeline()` calls.
|
||||
else:
|
||||
Advisor (doubt): It's false... something went wrong.
|
||||
Advisor (doubt): Did you skip timeline A?
|
||||
|
||||
[wait time="1.5"]
|
||||
|
||||
Advisor (pl5): Key insight: variables are shared GLOBAL state.
|
||||
Advisor (doubt): Any timeline can read or write any pre-declared variable.
|
||||
Advisor (pl5): This makes them perfect for world state, quest flags,\
|
||||
relationship scores, and inventory.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
jump gym_main/menu
|
||||
@@ -0,0 +1 @@
|
||||
uid://cvvvm15gfcw1g
|
||||
@@ -0,0 +1,33 @@
|
||||
# ============================================================
|
||||
# Pattern 4: Signal Event → GDScript → New Timeline
|
||||
# ============================================================
|
||||
# This timeline emits a signal. GDScript in gym_controller.gd
|
||||
# catches it and starts the target timeline.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
|
||||
Miko (neutral): This pattern uses a signal to bridge timelines.
|
||||
Miko (smile): I'm going to emit a signal, and GDScript will react.
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
Miko (joy): Emitting signal: "demo_chain"!
|
||||
|
||||
# This emits Dialogic.signal_event("demo_chain")
|
||||
# gym_controller.gd listens for this and starts
|
||||
# gym_pattern4_signal_target.dtl
|
||||
[signal arg=demo_chain]
|
||||
|
||||
# This timeline continues until it ends...
|
||||
Miko (neutral): I'll keep going while the signal fires in the background.
|
||||
|
||||
[wait time="0.5"]
|
||||
|
||||
Miko (smile): ...and now I'm done here!
|
||||
|
||||
# That's it. The signal handler in code started
|
||||
# gym_pattern4_signal_target.dtl which will take over.
|
||||
[end_timeline]
|
||||
@@ -0,0 +1 @@
|
||||
uid://b18khgix70in4
|
||||
@@ -0,0 +1,34 @@
|
||||
# ============================================================
|
||||
# Pattern 4 Target: Started by GDScript Signal Handler
|
||||
# ============================================================
|
||||
# This timeline was started by gym_controller.gd in response
|
||||
# to the "demo_chain" signal from gym_pattern4_signal_chain.dtl.
|
||||
# ============================================================
|
||||
|
||||
join Advisor right
|
||||
|
||||
Advisor (surprise): I was started by GDScript!
|
||||
Advisor (pl5): The `dialogic.signal_event` signal fired with\
|
||||
the argument "demo_chain".
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
Advisor (pl5): The controller script caught it and called\
|
||||
`Dialogic.start_timeline("gym_pattern4_signal_target")`.
|
||||
|
||||
Advisor (doubt): This is the most flexible pattern because...
|
||||
Advisor (pl5): ...GDScript can do ANYTHING before starting the next timeline:\
|
||||
load scenes, animate sprites, play audio, check conditions...
|
||||
|
||||
[wait time="2.0"]
|
||||
|
||||
Advisor (pl5): Signal args can be strings (like "demo_chain")\
|
||||
or full dictionaries for complex data.
|
||||
|
||||
Advisor (surprise): One caveat: `start_timeline()` doesn't use the jump stack.
|
||||
Advisor (pl5): So there's no automatic `return` path. You control flow in code.
|
||||
|
||||
[wait time="1.5"]
|
||||
|
||||
Advisor (pl5): Ready to go back to the hub?
|
||||
jump gym_main/menu
|
||||
@@ -0,0 +1 @@
|
||||
uid://dabm67j4647sw
|
||||
@@ -0,0 +1,37 @@
|
||||
# ============================================================
|
||||
# Pattern 5: Choice Routes to Different Timelines
|
||||
# ============================================================
|
||||
# Each choice branch doesn't just set a variable —
|
||||
# it JUMPS to a completely different timeline.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
join Advisor right
|
||||
|
||||
Advisor (doubt): This is a crossroads. Each choice leads to a different\
|
||||
destination.
|
||||
|
||||
Miko (neutral): Where should we go?
|
||||
|
||||
- The sunny meadow
|
||||
Miko (joy): I love flowers! Let's go to the meadow.
|
||||
Advisor (pl5): Fresh air will do us good.
|
||||
jump gym_main/menu
|
||||
|
||||
- The dark cave
|
||||
Miko (doubt): It's so dark in there... but I'm curious.
|
||||
Advisor (doubt): Bring a torch. We don't know what lurks inside.
|
||||
jump gym_main/menu
|
||||
|
||||
- Back to the hub
|
||||
Miko (smile): Good idea. I've seen enough crossroads.
|
||||
Advisor (pl5): A safe choice.
|
||||
jump gym_main/menu
|
||||
|
||||
# Note: In a real game, these would jump to:
|
||||
# jump meadow_scene/start
|
||||
# jump cave_scene/start
|
||||
# jump hub_scene/main
|
||||
# But here we just return to the hub for the demo.
|
||||
@@ -0,0 +1 @@
|
||||
uid://cmqgipcskfvb7
|
||||
@@ -0,0 +1,39 @@
|
||||
# ============================================================
|
||||
# Pattern 6: Condition-Based Timeline Routing
|
||||
# ============================================================
|
||||
# Variable values automatically decide which timeline to run.
|
||||
# This is great for: time-of-day, reputation systems, quest states.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
join Advisor right
|
||||
|
||||
Advisor (pl5): This pattern routes to different timelines\
|
||||
based on variable values.
|
||||
|
||||
Miko (neutral): Let's check the variables and see where we go...
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
# These variables are set/cleared by the hub menu
|
||||
# and by exploring different patterns
|
||||
|
||||
if {Gym.demo_flag}:
|
||||
Miko (joy): `gym_demo_flag` is TRUE! You explored pattern 3.
|
||||
Miko (smile): That means I'll route to the "explored" path.
|
||||
jump gym_main/menu
|
||||
elif {Museum.met_merchant}:
|
||||
Miko (neutral): You've met the merchant before!
|
||||
Advisor (pl5): We'll take a different route for returning players.
|
||||
jump gym_main/menu
|
||||
else:
|
||||
Miko (doubt): No flags are set. This is your first time here.
|
||||
Advisor (doubt): We'll take the default path.
|
||||
jump gym_main/menu
|
||||
|
||||
# In a real game, these would jump to different scene timelines:
|
||||
# jump explored_content/
|
||||
# jump returning_player/
|
||||
# jump first_time/
|
||||
@@ -0,0 +1 @@
|
||||
uid://bd4jdammxoo55
|
||||
@@ -0,0 +1,48 @@
|
||||
# ============================================================
|
||||
# Pattern 7: Player Text Input Drives Next Timeline
|
||||
# ============================================================
|
||||
# The player types text that gets stored in a variable.
|
||||
# That variable then determines what happens next.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
join Advisor right
|
||||
|
||||
Advisor (pl5): This pattern captures player text input\
|
||||
and uses it to personalize the next timeline.
|
||||
|
||||
Miko (smile): Let me ask you something...
|
||||
|
||||
[wait time="0.5"]
|
||||
|
||||
[text_input text="What is your name, traveler?" var="Gym.player_name" placeholder="Enter your name..." default="Hero" allow_empty="false"]
|
||||
|
||||
Miko (joy): {Gym.player_name}? What a great name!
|
||||
|
||||
[wait time="0.5"]
|
||||
|
||||
Miko (neutral): Now watch — I can use your name to route you to\
|
||||
different content.
|
||||
|
||||
if {Gym.player_name} == "Miko":
|
||||
Miko (surprise): Wait... that's MY name!
|
||||
Advisor (surprise): An impostor? Or a coincidence?
|
||||
Miko (anger): I'll be watching you, {Gym.player_name}...
|
||||
elif {Gym.player_name} == "Hero":
|
||||
Miko (smile): Classic choice. Very heroic!
|
||||
Advisor (pl5): Predictable, but reliable.
|
||||
else:
|
||||
Miko (joy): {Gym.player_name}, that's unique! I've never heard that before.
|
||||
Advisor (pl5): A name to remember.
|
||||
|
||||
[wait time="1.5"]
|
||||
|
||||
Advisor (pl5): In a real game, the name would persist across ALL timelines.
|
||||
Advisor (pl5): You could reference `{Gym.player_name}` in any dialog.
|
||||
Miko (smile): NPCs could greet you by name, quest logs would use it...
|
||||
|
||||
[wait time="1.0"]
|
||||
|
||||
jump gym_main/menu
|
||||
@@ -0,0 +1 @@
|
||||
uid://dvlhmbl62bltq
|
||||
@@ -0,0 +1,72 @@
|
||||
# ============================================================
|
||||
# Pattern 8: Dynamic Label Jump (Variable as Jump Target)
|
||||
# ============================================================
|
||||
# The value of a variable is used as the label to jump to.
|
||||
# This creates a state-machine-like flow controlled by data.
|
||||
# ============================================================
|
||||
|
||||
label start
|
||||
|
||||
join Miko center
|
||||
|
||||
Miko (neutral): This pattern uses a variable's VALUE as the jump target.
|
||||
Miko (smile): Let me show you with a time-of-day example.
|
||||
|
||||
# Set the variable that will drive the jump
|
||||
# In a real game, this might be set by a clock system
|
||||
set {Gym.time_of_day} = "morning"
|
||||
|
||||
Miko (neutral): The variable `Gym.time_of_day` is set to "{Gym.time_of_day}".
|
||||
Miko (joy): Now watch: I'll jump to a label with that exact name.
|
||||
|
||||
# This jump target is resolved at runtime from the variable
|
||||
jump {Gym.time_of_day}
|
||||
|
||||
# ---- Labels act as states ----
|
||||
|
||||
label morning
|
||||
Miko (joy): Good morning! The sun is rising over the hills.
|
||||
Miko (smile): Birds are singing, and the air is crisp and fresh.
|
||||
[wait time="1.5"]
|
||||
jump after_demo
|
||||
|
||||
label afternoon
|
||||
Miko (neutral): It's a warm afternoon. The sun is high.
|
||||
Miko (smile): A perfect time for adventuring!
|
||||
[wait time="1.5"]
|
||||
jump after_demo
|
||||
|
||||
label evening
|
||||
Miko (doubt): The sun is setting. Shadows grow long.
|
||||
Miko (neutral): We should find shelter before night falls.
|
||||
[wait time="1.5"]
|
||||
jump after_demo
|
||||
|
||||
label night
|
||||
Miko (shock): It's dark out here! The stars are beautiful but eerie.
|
||||
Miko (doubt): I can barely see the path ahead...
|
||||
[wait time="1.5"]
|
||||
jump after_demo
|
||||
|
||||
# ---- Wrap-up ----
|
||||
|
||||
label after_demo
|
||||
|
||||
join Advisor right
|
||||
|
||||
Advisor (pl5): The jump target was `{Gym.time_of_day}` — resolved at runtime!
|
||||
Advisor (pl5): This is incredibly powerful for:
|
||||
Advisor (doubt): • Time-of-day systems
|
||||
Advisor (doubt): • Quest state machines
|
||||
Advisor (doubt): • Chapter/scene routing
|
||||
Advisor (doubt): • Any narrative that follows a data-driven flow
|
||||
|
||||
[wait time="2.0"]
|
||||
|
||||
Miko (smile): You can also combine it with timeline names.
|
||||
Advisor (pl5): Use one variable for the timeline and another for the label.
|
||||
Advisor (surprise): Making it possible to route ANYWHERE from a variable.
|
||||
|
||||
[wait time="1.5"]
|
||||
|
||||
jump gym_main/menu
|
||||
@@ -0,0 +1 @@
|
||||
uid://bxrdy4ww5ewbf
|
||||
@@ -8,9 +8,9 @@
|
||||
# ============================================================
|
||||
|
||||
# ---- SETUP: Variables --------------------------------------
|
||||
set {coins} = 0
|
||||
set {player_has_key} = false
|
||||
set {met_merchant} = false
|
||||
set {Museum.coins} = 0
|
||||
set {Museum.player_has_key} = false
|
||||
set {Museum.met_merchant} = false
|
||||
|
||||
# ---- LABEL: opening ----------------------------------------
|
||||
label opening
|
||||
@@ -31,7 +31,7 @@ Advisor (pl5): Remember, [color=red]colored text[/color] works as well.
|
||||
Advisor (pl5): [pause=1.0] ...and so do timed pauses.
|
||||
|
||||
# ---- CONDITION: Variable-based branching ------------------
|
||||
if {coins} == 0:
|
||||
if {Museum.coins} == 0:
|
||||
Miko (doubt): I don't have any coins yet. Let's find some!
|
||||
Advisor (doubt): We should look around carefully.
|
||||
else:
|
||||
@@ -39,7 +39,7 @@ else:
|
||||
Advisor (pl5): Good. That will come in handy.
|
||||
|
||||
# ---- VARIABLE: Set a variable -----------------------------
|
||||
set {coins} = 10
|
||||
set {Museum.coins} = 10
|
||||
|
||||
Miko (joy): [shake]Look![/shake] I just found 10 coins.
|
||||
|
||||
@@ -55,11 +55,11 @@ Merchant (happy): Welcome, traveler! Would you like to see my wares?
|
||||
|
||||
# ---- CHOICE: Branching dialog -----------------------------
|
||||
- Yes, show me what you've got!
|
||||
set {met_merchant} = true
|
||||
set {Museum.met_merchant} = true
|
||||
Miko (joy): Absolutely, show me your best items!
|
||||
Merchant (happy): Excellent choice! I have potions, scrolls, and enchanted blades.
|
||||
Merchant (blink): That will be {10/15/20} gold coins, please!
|
||||
set {coins} = -10
|
||||
set {Museum.coins} = -10
|
||||
Miko (shock): Here you go! That was expensive...
|
||||
Advisor (blink): Did you really have to spend all our coins?
|
||||
|
||||
@@ -68,10 +68,10 @@ Merchant (happy): Welcome, traveler! Would you like to see my wares?
|
||||
Merchant (happy): Suit yourself. The door is always open.
|
||||
Advisor (pl5): A wise decision.
|
||||
|
||||
- Use the ancient key to barter. | [if {player_has_key}]
|
||||
- Use the ancient key to barter. | [if {Museum.player_has_key}]
|
||||
Miko (smile): I have this ancient key. Will you trade for it?
|
||||
Merchant (surprise): [shake]A key from the old kingdom?![/shake] Take anything you want!
|
||||
set {player_has_key} = false
|
||||
set {Museum.player_has_key} = false
|
||||
Miko (joy): That was a great deal!
|
||||
Advisor (surprise): You traded the key?! That was reckless...
|
||||
|
||||
@@ -106,7 +106,7 @@ Miko (neutral): You're right, but we have to check it out.
|
||||
Advisor (pl5): Very well. I'll follow your lead.
|
||||
|
||||
# ---- CONDITION with ELSE ----------------------------------
|
||||
if {player_has_key}:
|
||||
if {Museum.player_has_key}:
|
||||
Miko (joy): Good thing we still have the ancient key. Let's open it!
|
||||
Advisor (surprise): The key glows faintly as you approach.
|
||||
Miko (smile): Here goes nothing...
|
||||
|
||||
@@ -27,6 +27,19 @@ directories/dch_directory={
|
||||
"Raptor": "res://docs/museums/dialogic/Raptor.dch"
|
||||
}
|
||||
directories/dtl_directory={
|
||||
"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_target": "res://docs/gyms/cross-timeline-interactions/gym_pattern1_target.dtl",
|
||||
"gym_pattern2_caller": "res://docs/gyms/cross-timeline-interactions/gym_pattern2_caller.dtl",
|
||||
"gym_pattern2_subroutine": "res://docs/gyms/cross-timeline-interactions/gym_pattern2_subroutine.dtl",
|
||||
"gym_pattern3_variable_chain_A": "res://docs/gyms/cross-timeline-interactions/gym_pattern3_variable_chain_A.dtl",
|
||||
"gym_pattern3_variable_chain_B": "res://docs/gyms/cross-timeline-interactions/gym_pattern3_variable_chain_B.dtl",
|
||||
"gym_pattern4_signal_chain": "res://docs/gyms/cross-timeline-interactions/gym_pattern4_signal_chain.dtl",
|
||||
"gym_pattern4_signal_target": "res://docs/gyms/cross-timeline-interactions/gym_pattern4_signal_target.dtl",
|
||||
"gym_pattern5_choice_jump": "res://docs/gyms/cross-timeline-interactions/gym_pattern5_choice_jump.dtl",
|
||||
"gym_pattern6_condition_router": "res://docs/gyms/cross-timeline-interactions/gym_pattern6_condition_router.dtl",
|
||||
"gym_pattern7_text_input": "res://docs/gyms/cross-timeline-interactions/gym_pattern7_text_input.dtl",
|
||||
"gym_pattern8_dynamic_label": "res://docs/gyms/cross-timeline-interactions/gym_pattern8_dynamic_label.dtl",
|
||||
"raptor_showcase": "res://docs/museums/dialogic/raptor_showcase.dtl",
|
||||
"start_timeline": "res://docs/museums/dialogic/start_timeline.dtl"
|
||||
}
|
||||
@@ -35,10 +48,18 @@ glossary/default_case_sensitive=true
|
||||
layout/style_list=[]
|
||||
layout/default_style="Default"
|
||||
variables={
|
||||
"Gym": {
|
||||
"demo_flag": false,
|
||||
"player_name": "Hero",
|
||||
"sub_value": 0,
|
||||
"time_of_day": "morning"
|
||||
},
|
||||
"Museum": {
|
||||
"coins": 0,
|
||||
"met_merchant": false,
|
||||
"player_has_key": true
|
||||
}
|
||||
}
|
||||
extensions_folder="res://addons/dialogic_additions"
|
||||
text/letter_speed=0.01
|
||||
text/initial_text_reveal_skippable=true
|
||||
|
||||
Reference in New Issue
Block a user