Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation to the Background and Audio subsystem #2122

Merged
merged 21 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 39 additions & 25 deletions addons/dialogic/Core/DialogicGameHandler.gd
Original file line number Diff line number Diff line change
Expand Up @@ -22,64 +22,77 @@ enum States {
enum ClearFlags {
FULL_CLEAR = 0, ## Clears all subsystems
KEEP_VARIABLES = 1, ## Clears all subsystems and info except for variables
TIMLEINE_INFO_ONLY = 2 ## Doesn't clear subsystems but current timeline and index
TIMELINE_INFO_ONLY = 2 ## Doesn't clear subsystems but current timeline and index
}

## Reference to the currently executed timeline.
var current_timeline: DialogicTimeline = null
## Copy of the [member current_timeline]'s events.
var current_timeline_events: Array = []

## Index of the event the timeline handeling is currently at.
## Index of the event the timeline handling is currently at.
var current_event_idx: int = 0
## Contains all information that subsystems consider
## relevant for the current situation
## Contains all information that subsystems consider relevant for
## the current situation
var current_state_info: Dictionary = {}
## Current state (see [member States] enum)

## Current state (see [member States] enum).
var current_state := States.IDLE:
get:
return current_state

set(new_state):
current_state = new_state
emit_signal('state_changed', new_state)
state_changed.emit(new_state)

## Emitted when [member current_state] change.
signal state_changed(new_state:States)

## When `true`, many dialogic processes won't continue until it's false again.
## When `true`, many dialogic processes won't continue until it's `false` again.
var paused := false:
set(value):
paused = value

if paused:

for subsystem in get_children():

if subsystem is DialogicSubsystem:
(subsystem as DialogicSubsystem).pause()

dialogic_paused.emit()

else:
for subsystem in get_children():

if subsystem is DialogicSubsystem:
(subsystem as DialogicSubsystem).resume()

dialogic_resumed.emit()

## Emitted when [member paused] changes to true.
## Emitted when [member paused] changes to `true`.
signal dialogic_paused
## Emitted when [member paused] changes to false.
## Emitted when [member paused] changes to `false`.
signal dialogic_resumed


## Emitted when dialog ends (by a timeline end being reached OR end_timeline() being called).
## Emitted when the timeline ends.
## This can be a timeline ending or [method end_timeline] being called.
signal timeline_ended
## Emitted when a timeline was started with [method start] or [method start_timeline].
## Emitted when a timeline starts or with either [method start] or
CakeVR marked this conversation as resolved.
Show resolved Hide resolved
## [method start_timeline] being invoked.
signal timeline_started
## Emitted when an event has just been executed (not necessarily when the event finished).
signal event_handled(resource:DialogicEvent)
## Emitted when an event starts being executed.
## The event may not have finished executing yet.
signal event_handled(resource: DialogicEvent)

## Emitted when the `Signal Event` was reached
signal signal_event(argument:Variant)
## Emitted when `[signal]` effect was reached in text.
signal text_signal(argument:String)
## Emitted when a [class SignalEvent] event was reached.
signal signal_event(argument: Variant)
## Emitted when a signal event gets fired from a [class TextEvent] event.
signal text_signal(argument: String)


# Careful, this section is repopulated automatically at certain moments
# Careful, this section is repopulated automatically at certain moments.
#region SUBSYSTEMS

var Audio := preload("res://addons/dialogic/Modules/Audio/subsystem_audio.gd").new():
Expand Down Expand Up @@ -227,13 +240,13 @@ func preload_timeline(timeline_resource:Variant) -> Variant:

## Clears and stops the current timeline.
func end_timeline() -> void:
clear(ClearFlags.TIMLEINE_INFO_ONLY)
clear(ClearFlags.TIMELINE_INFO_ONLY)
_on_timeline_ended()
timeline_ended.emit()


## Handles the next event.
func handle_next_event(ignore_argument:Variant = "") -> void:
func handle_next_event(_ignore_argument: Variant = "") -> void:
handle_event(current_event_idx+1)


Expand Down Expand Up @@ -269,12 +282,13 @@ func handle_event(event_index:int) -> void:
event_handled.emit(current_timeline_events[event_index])


## Resets dialogics state fully or partially.
## By using the clear flags from the [member ClearFlags] enum you can specify what info should be kept.
## For example at timeline end usually it doesn't clear node or subsystem info
func clear(clear_flags:=ClearFlags.FULL_CLEAR) -> bool:
## Resets Dialogic's state fully or partially.
## By using the clear flags from the [member ClearFlags] enum you can specify
## what info should be kept.
## For example, at timeline end usually it doesn't clear node or subsystem info.
func clear(clear_flags := ClearFlags.FULL_CLEAR) -> bool:

if !clear_flags & ClearFlags.TIMLEINE_INFO_ONLY:
if !clear_flags & ClearFlags.TIMELINE_INFO_ONLY:
for subsystem in get_children():
if subsystem is DialogicSubsystem:
(subsystem as DialogicSubsystem).clear_game_state(clear_flags)
Expand Down
8 changes: 4 additions & 4 deletions addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ func _ready() -> void:
get_tree().quit()
DialogicUtil.autoload().start(current_timeline)
DialogicUtil.autoload().timeline_ended.connect(get_tree().quit)
DialogicUtil.autoload().signal_event.connect(recieve_event_signal)
DialogicUtil.autoload().text_signal.connect(recieve_text_signal)
DialogicUtil.autoload().signal_event.connect(receive_event_signal)
DialogicUtil.autoload().text_signal.connect(receive_text_signal)

func recieve_event_signal(argument:String) -> void:
func receive_event_signal(argument:String) -> void:
print("[Dialogic] Encountered a signal event: ", argument)

func recieve_text_signal(argument:String) -> void:
func receive_text_signal(argument:String) -> void:
print("[Dialogic] Encountered a signal in text: ", argument)

func _input(event:InputEvent) -> void:
Expand Down
59 changes: 50 additions & 9 deletions addons/dialogic/Modules/Audio/subsystem_audio.gd
Original file line number Diff line number Diff line change
@@ -1,22 +1,57 @@
extends DialogicSubsystem

## Subsystem that manages music and sounds.

signal music_started(info:Dictionary)
signal sound_started(info:Dictionary)

## Subsystem for managing background music and one-shot sound effects.
##
## This subsystem has many different helper methods for managing audio
## in your timeline.
## For instance, you can listen to music changes via [signal music_started].


## Whenever a new background music is started, this signal is emitted and
## contains a dictionary with the following keys: [br]
## [br]
## Key | Value Type | Value [br]
## ----------- | ------------- | ----- [br]
## `path` | [type String] | The path to the audio resource file. [br]
## `volume` | [type float] | The volume of the audio resource that will be set to the [member base_music_player]. [br]
## `audio_bus` | [type String] | The audio bus name that the [member base_music_player] will use. [br]
## `loop` | [type bool] | Whether the audio resource will loop or not once it finishes playing. [br]
signal music_started(info: Dictionary)


## Whenever a new sound effect is set, this signal is emitted and contains a
## dictionary with the following keys: [br]
## [br]
## Key | Value Type | Value [br]
## ----------- | ------------- | ----- [br]
## `path` | [type String] | The path to the audio resource file. [br]
## `volume` | [type float] | The volume of the audio resource that will be set to [member base_sound_player]. [br]
## `audio_bus` | [type String] | The audio bus name that the [member base_sound_player] will use. [br]
## `loop` | [type bool] | Whether the audio resource will loop or not once it finishes playing. [br]
signal sound_started(info: Dictionary)


## Audio player base duplicated to play background music.
Jowan-Spooner marked this conversation as resolved.
Show resolved Hide resolved
##
## Background music is long audio.
var base_music_player := AudioStreamPlayer.new()
## Audio player base, that will be duplicated to play sound effects.
##
## Sound effects are short audio.
var base_sound_player := AudioStreamPlayer.new()


#region STATE
####################################################################################################

func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
## Clears the state on this subsystem and stops all sounds.
CakeVR marked this conversation as resolved.
Show resolved Hide resolved
##
## If you want to stop sounds only, use [method stop_all_sounds].
func clear_game_state(clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
update_music()
stop_all_sounds()


## Loads the state on this subsystem from the current state info.
func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
if load_flag == LoadFlags.ONLY_DNODES:
return
Expand All @@ -27,11 +62,13 @@ func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
update_music(info.path, info.volume, info.audio_bus, 0, info.loop)


## Pauses playing audio.
func pause() -> void:
for child in get_children():
child.stream_paused = true


## Resumes playing audio.
func resume() -> void:
for child in get_children():
child.stream_paused = false
Expand Down Expand Up @@ -85,12 +122,13 @@ func update_music(path := "", volume := 0.0, audio_bus := "Master", fade_time :=
fader.tween_callback(prev_node.queue_free)


## Whether music is playing.
func has_music() -> bool:
return !dialogic.current_state_info.get('music', {}).get('path', '').is_empty()


## Plays a given sound file.
func play_sound(path:String, volume := 0.0, audio_bus := "Master", loop := false) -> void:
func play_sound(path: String, volume := 0.0, audio_bus := "Master", loop := false) -> void:
if base_sound_player != null and !path.is_empty():
sound_started.emit({'path':path, 'volume':volume, 'audio_bus':audio_bus, 'loop':loop})
var new_sound_node := base_sound_player.duplicate()
Expand All @@ -110,6 +148,7 @@ func play_sound(path:String, volume := 0.0, audio_bus := "Master", loop := false
new_sound_node.finished.connect(new_sound_node.queue_free)


## Stops all audio.
func stop_all_sounds() -> void:
for node in get_children():
if node == base_sound_player:
Expand All @@ -118,7 +157,9 @@ func stop_all_sounds() -> void:
node.queue_free()


func interpolate_volume_linearly(value:float, node:Node) -> void:
## Converts a linear loudness value to decibel and sets that volume to
## the given [param node].
func interpolate_volume_linearly(value: float, node: Node) -> void:
node.volume_db = linear_to_db(value)


Expand Down
36 changes: 26 additions & 10 deletions addons/dialogic/Modules/Background/subsystem_backgrounds.gd
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
extends DialogicSubsystem

## Subsystem for managing backgrounds.

signal background_changed(info:Dictionary)


##
## This subsystem has many different helper methods for managing backgrounds.
## For instance, you can listen to background changes via
## [signal background_changed].


## Whenever a new background is set, this signal is emitted and contains a
## dictionary with the following keys: [br]
## [br]
## Key | Value Type | Value [br]
## ----------- | ------------- | ----- [br]
## `scene` | [type String] | The scene path of the new background. [br]
## `argument` | [type String] | Information given to the background on its update routine. [br]
## `fade_time` | [type float] | The time the background may take to transition in. [br]
## `same_scene`| [type bool] | If the new background uses the same Godot scene. [br]
signal background_changed(info: Dictionary)

## The default background scene Dialogic will use.
var default_background_scene: PackedScene = load(get_script().resource_path.get_base_dir().path_join('DefaultBackgroundScene/default_background.tscn'))
## The default transition Dialogic will use.
var default_transition: String = get_script().resource_path.get_base_dir().path_join("Transitions/Defaults/simple_fade.gd")


#region STATE
####################################################################################################

func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR):
## Empties the current background state.
func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
update_background()


func load_game_state(load_flag:=LoadFlags.FULL_LOAD):
## Loads the background state from the current state info.
func load_game_state(_load_flag := LoadFlags.FULL_LOAD) -> void:
update_background(dialogic.current_state_info.get('background_scene', ''), dialogic.current_state_info.get('background_argument', ''), 0.0, default_transition, true)

#endregion
Expand Down Expand Up @@ -128,7 +143,8 @@ func _on_transition_finished(background_node:DialogicNode_BackgroundHolder, tran
background_node.color = Color.TRANSPARENT
transition_node.queue_free()


## Adds sub-viewport with the given background scene as child to
## Dialogic scene.
func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder) -> SubViewportContainer:
var v_con := SubViewportContainer.new()
var viewport := SubViewport.new()
Expand Down Expand Up @@ -160,7 +176,7 @@ func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder

return v_con


## Whether a background is set.
func has_background() -> bool:
return !dialogic.current_state_info.get('background_scene', '').is_empty() or !dialogic.current_state_info.get('background_argument','').is_empty()

Expand Down
12 changes: 9 additions & 3 deletions addons/dialogic/Modules/Core/subsystem_input.gd
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
extends DialogicSubsystem
## Subsystem that handles input, Auto-Advance, and skipping.
##
## This subsystem can be accessed via GDScript: `Dialogic.Inputs`.

## Subsystem that handles input, autoadvance & skipping.

signal dialogic_action_priority
signal dialogic_action

## Whenever the Auto-Skip timer finishes, this signal is emitted.
## Configure Auto-Skip settings via [member auto_skip].
signal autoskip_timer_finished


Expand All @@ -18,7 +23,8 @@ var auto_advance : DialogicAutoAdvance = null
#region SUBSYSTEM METHODS
################################################################################

func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:

func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
if not is_node_ready():
await ready

Expand Down Expand Up @@ -142,7 +148,7 @@ func _on_autoskip_toggled(enabled: bool) -> void:
## Handles fine-grained Auto-Skip logic.
## The [method _process] method allows for a more precise timer than the
## [Timer] class.
func _process(delta):
func _process(delta: float) -> void:
if _auto_skip_timer_left > 0:
_auto_skip_timer_left -= delta

Expand Down
19 changes: 13 additions & 6 deletions addons/dialogic/Modules/Save/subsystem_save.gd
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
extends DialogicSubsystem

### Subsystem that manages saving and loading data.
## Subsystem to save and load game states.
##
## This subsystem has many different helper methods to save Dialogic or custom
## game data to named save slots.
##
## You can listen to saves via [signal saved]. \
## If you want to save, you can call [method save]. \


## Emitted when a save happened.
## The [param info] contains the following keys:
## - slot_name: The `String` name of the slot that the game state was saved to.
## - is_autosave: `true` if the save was an autosave.
## Emitted when a save happened with the following info:
## [br]
## Key | Value Type | Value [br]
## ----------- | ------------- | ----- [br]
## `slot_name` | [type String] | The name of the slot that the game state was saved to. [br]
## `is_autosave` | [type bool] | `true`, if the save was an autosave. [br]
signal saved(info: Dictionary)


Expand Down
Loading
Loading