Chapter Events

Chapter Events are scripted sequences that trigger when a Player reaches a certain spot on the map.

Goals

Facilitate the creation of narrative scenes with reusable actions and a flexible structure.

How to create a Chapter Event

The first step to creating a Chapter Event is to create a ChapterEventPlayer and a ChapterEventTrigger.

All levels contain a proposed structure for setting up Narrative nodes:

  • Level
    • Narrative
      • Triggers: where ChapterEventTriggers should be set
      • Paths: where Path2Ds should be set
      • Players: where ChapterEventPlayers should be set

ChapterEventPlayer

To create a ChapterEventPlayer, simply add a new Node to the scene and choose ChapterEventPlayer.

In the inspector, you may change some of this event's behavior by modifying these properties:

  • ID: Must be a unique id (unique in the whole game), as it identifies the event in the player's save data. Following the placeholder suggestion (chapter_00/room_00/scene_00) should be enough;
  • Play Once: If true, this event will never trigger again after finished;
  • Is Interactive: If true, Player can interact (move, attack, etc.) during the event. If false, Player input is blocked and cutscene vignettes are triggered;
  • Trigger Path: Must point to this event's trigger.

Every child of this node must be a type of Event.

ChapterEventTrigger

To create a ChapterEventTrigger, instantiate it from the scene res://src/Narrative/ChapterEvent/ChapterEventTrigger.tscn - this scene has the base class with proper collision set.

You will need to add a CollisionShape2D node as child of the Trigger and set the area for triggering that event.

Adding Events

Events are added as nodes to the ChapterEventPlayer. You can see the full list of Events by opening the Create New Node dialog and searching for Event.

Every class that is a child of BaseChapterEvent can be used:

List of currently available events

EventAddNode

Adds a Node to the scene. Expects a path to a placeholder Position2D node and a PackedScene to instantiate.

EventCameraMove

Moves GameplayCamera along a curve. Expects a Path2D node that defines the movement.

EventCameraReturnToPlayer

Returns GameplayCamera to Player.

EventCameraSetPosition

Moves GameplayCamera instantanely to a set position. Expects a Node2D that defines the position.

EventChainParallel

Chains multiple events, playing them at the same time. Finishes when all events are finished.

Every child of this node must be a type of Event.

EventChainSequence

Chains multiple events, playing them in sequence, one after the other. Useful if you need to define a sequence inside of a EventChainParallel.

Every child of this node must be a type of Event.

EventNode2DMove

Moves any Node2D along a curve. Expects a Path2D node that defines the movement. Meant to be used to prototype scene while we don't have NPC or Enemy events.

EventPlayInnerMonologue

Plays an inner monologue.

EventPlayerMove

Moves the Player along a curve. Expects a Path2D node that defines the movement. Player will walk at its own speed.

EventRemoveNode

Removes a Node from the scene.

EventWaitForTimer

Waits for a timer.

How it works

A ChapterEventPlayer is responsible for executing a sequence of events in order. It must be connected to a ChapterEventTrigger. When its triggered signal is emitted, the ChapterEventPlayer will play its sequence.

When playing, ChapterEventPlayer iterates through its child nodes (each should extend BaseChapterEvent) and executes them. If an event isn't finished, the system will wait for a finished signal from the event node before proceeding to the next.

Both ChapterEventPlayer and BaseChapterEvent have started and finished signals if you need to track their execution.

All event scripts must have a custom class name - so that they are exposed in the "Create New Node" dialog - and should start with Event....

Creating a Custom Event

To create a new event, extend BaseChapterEvent and implement your _play() method. This method must return true or false, signaling if the event is completed.

Example of an instantaneous event:

class_name EventRemoveNode
extends BaseChapterEvent

export var node_path: NodePath

func _play() -> bool:
	var node: Node = get_node(node_path)
	node.get_parent().remove_child(node)
	node.queue_free()
	return true

If an event performs an action that takes time (e.g., animations), it must return false and call _finished() when complete.

Example of an asynchronous event:

class_name EventWaitForTimer
extends BaseChapterEvent

export var duration: float = 1.0

func _play() -> bool:
	var timer := get_tree().create_timer(duration)
	timer.connect("timeout", self, "_on_timer_timeout", [], CONNECT_ONESHOT)
	return false

func _on_timer_timeout() -> void:
	_finished()

With what script it communicates

Events can control: Player, GameplayCamera

Events can trigger: Inner Monologue

ChapterEventPlayer interacts with SaveDataHandler, to save if an event has been played yet.