Created
August 27, 2024 04:21
-
-
Save Capital-EX/9d2eaa46fe3465aca4149b5b24f2f2cf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# warning-ignore-all:void_assignment | |
# warning-ignore-all:return_value_discarded | |
extends Control | |
class_name Sequence | |
# todo - impelement prize screen | |
signal dialogue_shown() | |
signal option_pressed(id) | |
enum Commands { | |
FADE_OUT, FADE_IN, | |
CHAR_ENTER, CHAR_MOVE, CHAR_FLIP, CHAR_EXIT, CHAR_EMOTE, | |
HIDE_DIALOGUE, | |
APPEND_HISTORY, | |
WHEN_SEEN, SET_SEEN, | |
WHEN_FLAG, SET_FLAG, | |
WHEN, PICK, RUN, GOTO, | |
AWAIT_RUN, AWAIT_RESULT, AWAIT_SIGNAL, | |
AWAIT_INTERACTION, AWAIT_MINIGAME, | |
GIVE_PRIZE, GIVE_ITEM, TAKE_ITEM, | |
SET_BACKDROP, ADD_BACKDROP, REMOVE_BACKDROP, CLEAR_BACKDROP, | |
SAY, | |
# RESPOND is for when the player says something | |
# NEXT is for when the player needs to click next | |
# WAIT is for when the player only needs to wait for the dialogue to end | |
RESPOND, NEXT, WAIT, | |
CHANGE_STAT, SET_STAT, WHEN_STAT, | |
CHANGE_MUSIC, | |
SHOW_BLACKOUT, HIDE_BLACKOUT, | |
TRANSITION, END | |
} | |
enum { | |
POS_LEFT, | |
POS_CENTER, | |
POS_RIGHT, | |
POS_TOP, | |
POS_BOTTOM, | |
} | |
enum { | |
FADE_INVISIBLE, | |
FADE_VISIBLE, | |
} | |
const ButtonBase = preload("res://scenes/ui/misc/ButtonBase.tscn") | |
const ButtonPrize = null | |
const QMo8_24px = preload("res://fonts/QMo8_24px.tres") | |
const NoImage = preload("res://imgs/events/NoImage.png") | |
var entry_passage = "intro" | |
var entry_line = 0 | |
var entry_previous_state = {} | |
var opcode = [] | |
var ip = 0 | |
var running = false | |
var character_lookup = {} | |
var backdrop_lookup: Dictionary = {} | |
var skip_fade: bool = false | |
var fade_state: int = FADE_INVISIBLE | |
onready var Blackout: ColorRect = $Blackout | |
onready var BackgroundLayer: CanvasLayer = $SceneContainer/SceneViewport/BackgroundLayer | |
onready var CharacterLayer: CanvasLayer = $SceneContainer/SceneViewport/CharacterLayer | |
onready var DialogueContainer: Container = $DialogueContainer | |
onready var DialogueLabel: Label = $DialogueContainer/MarginContainer/DialogueBox/DialogueMargin/DialogueLabel | |
onready var OptionsContainer: Container = $MarginContainer/OptionsContainer | |
onready var ItemScreen: Control = $ItemScreen | |
onready var FadePlayer: AnimationPlayer = $BlackoutPanel/FadePlayer | |
onready var DialogueShower: Tween = $DialogueContainer/DailogShower | |
onready var SceneViewport: Viewport = $SceneContainer/SceneViewport | |
func set_entry(passage: String, line: int, previous_state: Dictionary) -> void: | |
prints(passage, line, previous_state) | |
entry_passage = passage | |
entry_line = line | |
entry_previous_state = previous_state | |
func _ready() -> void: | |
setup() | |
execute(entry_passage, entry_line) | |
func _input(event: InputEvent): | |
if event is InputEventMouseButton and event.is_pressed() and not event.is_echo(): | |
if DialogueShower.is_active(): | |
DialogueShower.seek(DialogueShower.get_runtime()) | |
if FadePlayer.is_playing(): | |
if FadePlayer.current_animation == "fade_out": | |
FadePlayer.playback_speed = 1000.0 | |
skip_fade = true | |
elif FadePlayer.current_animation == "fade_in": | |
FadePlayer.playback_speed = 1000.0 | |
skip_fade = false | |
func _exit_tree() -> void: | |
for character in character_lookup: | |
character_lookup[character].free() | |
for backdrop in backdrop_lookup: | |
backdrop_lookup[backdrop].free() | |
func _on_option_pressed(id: int) -> void: | |
$Click.play() | |
_clear_options() | |
emit_signal("option_pressed", id) | |
func setup() -> void: | |
push_warning("Setup function not overridden.") | |
func intro() -> void: | |
push_warning("Missing intro passage.") | |
func save() -> Dictionary: | |
var data = {} | |
for backdrop in backdrop_lookup: | |
if backdrop_lookup[backdrop].has_method('save') and backdrop_lookup[backdrop].is_ready: | |
data[backdrop] = backdrop_lookup[backdrop].save() | |
return data | |
func execute(entry: String, line: int = 0) -> void: | |
opcode = [] | |
ip = line | |
self.call(entry) | |
State.remember_current_passage(entry) | |
if ip != 0: | |
# this must be from an ActionType.RESTORE_STATE | |
# there exist no op code that results in an IP != 0 | |
# when execute is called. | |
var current_dialogue = State.fetch_current_dialogue() | |
var blackout_active = State.fetch_blackout_visible() | |
var active_backdrop_list = State.fetch_backdrop_list() | |
var active_character_list = State.fetch_character_list() | |
if not current_dialogue.empty(): | |
if 'text' in current_dialogue and not current_dialogue.text.empty(): | |
_say(current_dialogue.text, current_dialogue.position, current_dialogue.rate) | |
DialogueShower.seek(DialogueShower.get_runtime()) | |
if current_dialogue.hidden: | |
_hide_dialogue() | |
if blackout_active: | |
_show_blackout() | |
for backdrop in active_backdrop_list: | |
_add_backdrop(backdrop) | |
for backdrop in entry_previous_state: | |
prints(backdrop, entry_previous_state[backdrop]) | |
backdrop_lookup[backdrop].set_restore_data(entry_previous_state[backdrop]) | |
for character in active_character_list: | |
var data = active_character_list[character] | |
_enter(character, data.standing, data.facing, data.emote) | |
if opcode[ip].command != Commands.FADE_IN: | |
$BlackoutPanel.material.set_shader_param('offset', 0) | |
if not running: | |
interpret() | |
func clear() -> void: | |
for character in character_lookup: | |
character_lookup[character].free() | |
for backdrop in backdrop_lookup: | |
backdrop_lookup[backdrop].free() | |
func interpret() -> void: | |
running = true | |
while ip < len(opcode): | |
var co: GDScriptFunctionState | |
var op: Dictionary = opcode[ip] | |
State.remember_current_line(ip) | |
ip += 1 | |
co = eval(op) | |
if co: | |
yield(co, "completed") | |
if not opcode[-1].command != Commands.END: | |
push_warning("Sequence: end() was not called but there are no more instructions to run...") | |
end() | |
running = false | |
func eval(op) -> GDScriptFunctionState: | |
var co = null | |
match op.command: | |
Commands.FADE_IN: | |
co = _fade_in() | |
Commands.FADE_OUT: | |
co = _fade_out() | |
Commands.CHAR_ENTER: | |
co = _enter(op.character, op.standing, op.facing, op.emote) | |
Commands.CHAR_MOVE: | |
co = _move(op.character, op.standing) | |
Commands.CHAR_FLIP: | |
co = _face(op.character, op.facing) | |
Commands.CHAR_EXIT: | |
co = _exit(op.character) | |
Commands.CHAR_EMOTE: | |
co = _emote(op.character, op.emote) | |
Commands.HIDE_DIALOGUE: | |
co = _hide_dialogue() | |
Commands.APPEND_HISTORY: | |
co = _append_history(op.history) | |
Commands.WHEN_SEEN: | |
co = _when_seen(op.conds) | |
Commands.SET_SEEN: | |
co = _set_seen(op.passage) | |
Commands.WHEN_FLAG: | |
co = _when_flag(op.conds) | |
Commands.SET_FLAG: | |
co = _set_flag(op.flag) | |
Commands.WHEN: | |
co = _when(op.conds) | |
Commands.PICK: | |
if FADE_VISIBLE: push_warning("Sequencer: fade_out has not been called") | |
co = _pick(op.options) | |
Commands.RUN: | |
co = _run(op.object, op.method, op.args) | |
Commands.GOTO: | |
co = _goto(op.passage) | |
Commands.AWAIT_RUN: | |
co = _await_run(op. object, op.method, op.args) | |
Commands.AWAIT_RESULT: | |
co = _await_result(op.object, op.sig, op.callback, op.handler) | |
Commands.AWAIT_SIGNAL: | |
co = _await_signal(op.object, op.sig) | |
Commands.AWAIT_INTERACTION: | |
co = _await_interaction(op.id, op.callback, op.sig, op.handler) | |
Commands.AWAIT_MINIGAME: | |
co = _await_minigame(op.minigame, op.callback, op.handler) | |
Commands.GIVE_PRIZE: | |
co = _give_prize(op.options) | |
Commands.GIVE_ITEM: | |
co = _give_item(op.name) | |
Commands.TAKE_ITEM: | |
co = _take_item(op.id) | |
Commands.SET_BACKDROP: | |
co = _set_backdrop(op.id) | |
Commands.ADD_BACKDROP: | |
co = _add_backdrop(op.id) | |
Commands.REMOVE_BACKDROP: | |
co = _remove_backdrop(op.id) | |
Commands.CLEAR_BACKDROP: | |
co = _clear_backdrop() | |
Commands.SAY: | |
if FADE_VISIBLE: push_warning("Sequencer: fade_out has not been called") | |
co = _say(op.message, op.pos, op.rate) | |
Commands.RESPOND: | |
if FADE_VISIBLE: push_warning("Sequencer: fade_out has not been called") | |
co = _respond(op.message) | |
Commands.NEXT: | |
if FADE_VISIBLE: push_warning("Sequencer: fade_out has not been called") | |
co = _next() | |
Commands.WAIT: | |
co = _wait(op.delay) | |
Commands.CHANGE_STAT: | |
co = _change_stat(op.stat, op.delta) | |
Commands.SET_STAT: | |
co = _set_stat(op.stat, op.value) | |
Commands.WHEN_STAT: | |
co = _when_stat(op.conds) | |
Commands.CHANGE_MUSIC: | |
co = _change_music(op.track) | |
Commands.SHOW_BLACKOUT: | |
co = _show_blackout() | |
Commands.HIDE_BLACKOUT: | |
co = _hide_blackout() | |
Commands.TRANSITION: | |
co = _transition(op.to) | |
Commands.END: | |
co = _end() | |
return co | |
func character(character: String) -> void: | |
character_lookup[character] = Characters.get_character(character) | |
# ! This will need to be changed to the background loading system | |
func backdrop_image(id: String, texture: Texture) -> void: | |
var texture_rect: Sprite = Sprite.new() | |
texture_rect.centered = false | |
texture_rect.texture = texture | |
backdrop_lookup[id] = texture_rect | |
# ! This will need to be changed to the background loading system | |
func backdrop_scene(id: String, scene: PackedScene) -> void: | |
backdrop_lookup[id] = scene.instance() | |
func get_backdrop(backdrop_name) -> CanvasItem: | |
return backdrop_lookup.get(backdrop_name) | |
func fade_out() -> void: | |
opcode.push_back({ | |
command = Commands.FADE_OUT, | |
}) | |
func fade_in() -> void: | |
opcode.push_back({ | |
command = Commands.FADE_IN, | |
}) | |
func enter(character: String, | |
standing: int = Character.STANDING_LEFT, | |
facing: int = Character.FACING_RIGHT, | |
emote: String = "default") -> void: | |
opcode.push_back({ | |
command = Commands.CHAR_ENTER, | |
character = character, | |
standing = standing, | |
facing = facing, | |
emote = emote | |
}) | |
func move(character: String, standing: int) -> void: | |
opcode.push_back({ | |
command = Commands.CHAR_MOVE, | |
character = character, | |
standing = standing | |
}) | |
func face(character: String, facing: int) -> void: | |
opcode.push_back({ | |
command = Commands.CHAR_FLIP, | |
character = character, | |
facing = facing, | |
}) | |
func exit(character: String) -> void: | |
opcode.push_back({ | |
command = Commands.CHAR_EXIT, | |
character = character | |
}) | |
func emote(character: String, emote: String) -> void: | |
opcode.push_back({ | |
command = Commands.CHAR_EMOTE, | |
character = character, | |
emote = emote | |
}) | |
func hide_dialogue() -> void: | |
opcode.push_back({ | |
command = Commands.HIDE_DIALOGUE | |
}) | |
func append_history(history: String) -> void: | |
opcode.push_back({ | |
command = Commands.APPEND_HISTORY, | |
history = history | |
}) | |
func when_seen(conds: Array) -> void: | |
opcode.push_back({ | |
command = Commands.WHEN_SEEN, | |
conds = conds | |
}) | |
func set_seen(passage: String) -> void: | |
opcode.push_back({ | |
command = Commands.SET_SEEN, | |
passage = passage | |
}) | |
func when_flag(conds: Array) -> void: | |
opcode.push_back({ | |
command = Commands.WHEN_FLAG, | |
conds = conds | |
}) | |
func set_flag(flag: String) -> void: | |
opcode.push_back({ | |
command = Commands.SET_FLAG, | |
flag = flag | |
}) | |
func when(conds: Array) -> void: | |
opcode.push_back({ | |
command = Commands.WHEN_SEEN, | |
conds = conds | |
}) | |
func pick(options: Array) -> void: | |
opcode.push_back({ | |
command = Commands.PICK, | |
options = options | |
}) | |
func run(object: Object, method: String, args: Array = []) -> void: | |
opcode.push_back({ | |
command = Commands.RUN, | |
object = object, | |
method = method, | |
args = args | |
}) | |
func run_away(object: Object, method: String, args: Array = []) -> void: | |
opcode.push_back({ | |
command = Commands.WAIT_FOR, | |
object = object, | |
method = method, | |
args = args | |
}) | |
func goto(passage: String) -> void: | |
opcode.push_back({ | |
command = Commands.GOTO, | |
passage = passage | |
}) | |
# The await_run method is used to wait for a method to complete. This function assumes | |
# that method returns void. In oreder to wait for a result use await_result. | |
func await_run(object: Object, method: String, args: Array = []) -> void: | |
opcode.push_back({ | |
command = Commands.AWAIT_RUN, | |
object = object, | |
method = method, | |
args = args | |
}) | |
# ! If await_result, await_signal, await_interaction, or await_minigame need to change the story's opcode | |
# ! then they need to be the last instruction in a passage or call `execute('<passage name>')`. Otherwise, | |
# ! the instructions they push into the opcode will be executed *after* the remander of the current passage. | |
# ! However, this could be used to append additional dialogue to the end of the current passage. But, that | |
# ! may lead to confusion when debugging passages. | |
# ! | |
# ! Use with cation. | |
# The await_result method is used to wait for a signal to be emmitted. Additionally, this method allows the | |
# handler object to receive thee result of the signal. This method should be used to listen for | |
# the result of any method you want to run. | |
func await_result(object: Object, sig: String, callback: String, handler: Object = self) -> void: | |
opcode.push_back({ | |
command = Commands.AWAIT_RESULT, | |
object = object, | |
sig = sig, | |
callback = callback, | |
handler = handler | |
}) | |
# The await_signal method will pause the script execution until a specific signal has been emitted. | |
# This should never be used alone as the story will hang is the signal is never emitted. | |
func await_signal(object: Object, sig: String) -> void: | |
opcode.push_back({ | |
command = Commands.AWAIT_SIGNAL, | |
object = object, | |
sig = sig | |
}) | |
# The await_interaction method is used to begin a gameplay sequence in the middle of a story sequence. | |
# This is short hand for: | |
# ```gd | |
# set_backdrop(id) | |
# run(get_backdrop(id), 'start_interaction') | |
# await_result(get_backdrop(id), 'interaction_completed', callback, handler) | |
# ``` | |
# This method *requires* the backdrop specified by the id have the method 'start_interaction' | |
# and the signal 'interaction_completed' in order to succeed. Additionally, thie method will hang | |
# the story if 'interaction_completed' is never emitted. | |
func await_interaction(id: String, callback: String, sig: String = 'interaction_completed', handler: Object = self) -> void: | |
opcode.push_back({ | |
command = Commands.AWAIT_INTERACTION, | |
id = id, | |
callback = callback, | |
sig = sig, | |
handler = handler | |
}) | |
# The await_minigame method starts the specified minigame and then waits for the player to complete it. | |
# It then passes control to the specified handler. | |
func await_minigame(minigame: String, callback: String, handler: Object = self) -> void: | |
opcode.push_back({ | |
command = Commands.AWAIT_MINIGAME, | |
minigame = minigame, | |
callback = callback, | |
handler = handler, | |
}) | |
func give_prize(options = []) -> void: | |
opcode.push_back({ | |
command = Commands.GIVE_PRIZE, | |
options = options | |
}) | |
func give_item(name: String) -> void: | |
opcode.push_back({ | |
command = Commands.GIVE_ITEM, | |
name = name | |
}) | |
func take_item(id: int) -> void: | |
opcode.push_back({ | |
command = Commands.TAKE_ITEM, | |
id = id | |
}) | |
func set_backdrop(id: String) -> void: | |
opcode.push_back({ | |
command = Commands.SET_BACKDROP, | |
id = id | |
}) | |
func add_backdrop(id: String) -> void: | |
opcode.push_back({ | |
command = Commands.ADD_BACKDROP, | |
id = id | |
}) | |
func remove_backdrop(id: String) -> void: | |
opcode.push_back({ | |
command= Commands.REMOVE_BACKDROP, | |
id = id | |
}) | |
func clear_backdrop() -> void: | |
opcode.push_back({ | |
command = Commands.CLEAR_BACKDROP | |
}) | |
func say_left(message: String, rate = 1.0 / 60.0) -> void: | |
opcode.push_back({ | |
command = Commands.SAY, | |
pos = POS_LEFT, | |
message = message, | |
rate = rate | |
}) | |
func say_center(message: String, rate = 1.0 / 60.0) -> void: | |
opcode.push_back({ | |
command = Commands.SAY, | |
pos = POS_CENTER, | |
message = message, | |
rate = rate | |
}) | |
func say_right(message: String, rate = 1.0 / 60.0) -> void: | |
opcode.push_back({ | |
command = Commands.SAY, | |
pos = POS_RIGHT, | |
message = message, | |
rate = rate | |
}) | |
func say_top(message: String, rate = 1.0 / 60.0) -> void: | |
opcode.push_back({ | |
command = Commands.SAY, | |
pos = POS_TOP, | |
message = message, | |
rate = rate | |
}) | |
func say_bottom(message: String, rate = 1.0 / 60.0) -> void: | |
opcode.push_back({ | |
command = Commands.SAY, | |
pos = POS_BOTTOM, | |
message = message, | |
rate = rate | |
}) | |
func respond(message: String) -> void: | |
opcode.push_back({ | |
command = Commands.RESPOND, | |
message = message | |
}) | |
func next() -> void: | |
opcode.push_back({ | |
command = Commands.NEXT, | |
}) | |
func wait(delay: float) -> void: | |
opcode.push_back({ | |
command = Commands.WAIT, | |
delay = delay | |
}) | |
func change_stat(stat: String, delta: int) -> void: | |
opcode.push_back({ | |
command = Commands.CHANGE_STAT, | |
stat = stat, | |
delta = delta | |
}) | |
func set_stat(stat: String, value: int) -> void: | |
opcode.push_back({ | |
command = Commands.SET_STAT, | |
stat = stat, | |
value = value | |
}) | |
func when_stat(conds: Array) -> void: | |
opcode.push_back({ | |
command = Commands.WHEN_STAT, | |
conds = conds | |
}) | |
func change_music(track: String) -> void: | |
opcode.push_back({ | |
command = Commands.CHANGE_MUSIC, | |
track = track | |
}) | |
func show_blackout() -> void: | |
opcode.push_back({ | |
command = Commands.SHOW_BLACKOUT | |
}) | |
func hide_blackout() -> void: | |
opcode.push_back({ | |
command = Commands.HIDE_BLACKOUT | |
}) | |
func transition(to: String) -> void: | |
opcode.push_back({ | |
command = Commands.TRANSITION, | |
to = to | |
}) | |
func end(to: String = Events.NONE) -> void: | |
opcode.push_back({ | |
command = Commands.END, | |
to = to | |
}) | |
# * Private Methods: | |
func _show_dialogue() -> void: | |
DialogueContainer.show() | |
State.remember_dialogue_hidden(false) | |
func _clear_dialogue() -> void: | |
DialogueLabel.text = "" | |
State.forget_current_dialogue() | |
func _add_option(message: String = "") -> Button: | |
var button = ButtonBase.instance() | |
button.text = message | |
button.size_flags_horizontal = 0 | |
button.add_font_override("font", QMo8_24px) | |
button.connect("pressed", self, "_on_option_pressed", [OptionsContainer.get_child_count()]) | |
OptionsContainer.add_child(button) | |
return button | |
func _clear_options() -> void: | |
for child in OptionsContainer.get_children(): | |
child.queue_free() | |
func _fade_out() -> void: | |
fade_state = FADE_VISIBLE | |
FadePlayer.play("fade_out") | |
yield(FadePlayer, "animation_finished") | |
FadePlayer.playback_speed = 1.0 | |
func _fade_in() -> void: | |
FadePlayer.play("fade_in") | |
fade_state = FADE_INVISIBLE | |
if skip_fade: | |
FadePlayer.playback_speed = 1000.0 | |
yield(FadePlayer, "animation_finished") | |
FadePlayer.playback_speed = 1.0 | |
skip_fade = false | |
func _enter(character: String, standing: int, facing: int, emote: String) -> void: | |
CharacterLayer.add_child(character_lookup[character]) | |
character_lookup[character].standing = standing | |
character_lookup[character].facing = facing | |
character_lookup[character].current_emote = emote | |
State.remember_character(character, standing, facing, emote) | |
func _move(character: String, standing: int) -> void: | |
character_lookup[character].standing = standing | |
var c = character_lookup[character] | |
State.remember_character(c, c.standing, c.facing, c.current_emote) | |
func _face(character: String, facing: int) -> void: | |
character_lookup[character].facing = facing | |
var c = character_lookup[character] | |
State.remember_character(c, c.standing, c.facing, c.current_emote) | |
func _emote(character: String, emote: String) -> void: | |
character_lookup[character].current_emote = emote | |
var c = character_lookup[character] | |
State.remember_character(c.name, c.standing, c.facing, c.current_emote) | |
func _exit(character: String) -> void: | |
CharacterLayer.remove_child(character_lookup[character]) | |
State.forget_character(character) | |
func _hide_dialogue() -> void: | |
DialogueContainer.hide() | |
State.remember_dialogue_hidden(true) | |
func _append_history(history: String) -> void: | |
State.append_history(history) | |
func _when_seen(conds: Array) -> void: | |
for i in range(0, len(conds), 2): | |
if State.has_seen(conds[i]): | |
print(conds[i]) | |
execute(conds[i + 1]) | |
return | |
execute(conds[-1]) | |
func _set_seen(passage: String) -> void: | |
State.set_seen(passage) | |
# TODO - Implement flag system for when a sub-sequence has been seen | |
func _when_flag(_conds: Array) -> void: | |
pass | |
func _set_flag(_flag: String) -> void: | |
pass | |
# ! This may be useless with how the interpreter works. | |
func _when(conds: Array) -> void: | |
for i in range(0, len(conds), 2): | |
if conds[i]: | |
execute(conds[i + 1]) | |
return | |
execute(conds[-1]) | |
func _pick(options: Array) -> void: | |
for i in range(0, len(options), 2): | |
_add_option(options[i]) | |
var id = yield(self, "option_pressed") | |
_hide_dialogue() | |
execute(options[id * 2 + 1]) | |
func _run(object: Object, method: String, args: Array = []) -> void: | |
object.callv(method, args) | |
func _await_run(object: Object, method: String, args: Array = []) -> void: | |
var co = object.callv(method, args) | |
if co is GDScriptFunctionState: | |
yield(co, "completed") | |
else: | |
push_warning("The method %s is not a coroutine. Continuing execution of story." % method) | |
func _await_result(object: Object, sig: String, callback: String, handler: Object = self) -> void: | |
object.connect(sig, handler, callback, [], Object.CONNECT_ONESHOT) | |
yield(object, sig) | |
func _await_signal(object: Object, sig: String) -> void: | |
yield(object, sig) | |
func _await_interaction(id: String, callback: String, sig: String = 'interaction_completed', handler: Object = self) -> void: | |
var backdrop = backdrop_lookup[id] | |
State.disable_saving() | |
# Require to work around quirk where GUI nodes inside a view port will eat the input before | |
# the outer nodes have a chance to process it | |
SceneViewport.gui_disable_input = false | |
_set_backdrop(id) | |
_run(backdrop, 'start_interaction') | |
yield(_await_result(backdrop, sig, callback, handler), "completed") | |
State.enable_saving() | |
SceneViewport.gui_disable_input = true | |
func _await_minigame(minigame: String, callback: String, handler: Object = self) -> void: | |
State.connect('minigame_ended', handler, callback, [], CONNECT_ONESHOT) | |
State.set_current_minigame(minigame) | |
func _give_prize(options: Array) -> void: | |
yield(_fade_out(), 'completed') | |
ItemScreen.show() | |
for option in options: | |
ItemScreen.add_item(option) | |
yield(_fade_in(), 'completed') | |
var prize = yield(ItemScreen, 'item_selected') | |
_give_item(prize) | |
yield(_fade_out(), "completed") | |
ItemScreen.hide() | |
func _give_item(name: String) -> void: | |
State.add_item(name) | |
func _take_item(id: int) -> void: | |
State.remove_item(id) | |
func _goto(passage: String) -> void: | |
execute(passage) | |
func _set_backdrop(backdrop: String) -> void: | |
_clear_backdrop() | |
_add_backdrop(backdrop) | |
func _add_backdrop(backdrop: String) -> void: | |
BackgroundLayer.add_child(backdrop_lookup[backdrop]) | |
State.remember_backdrop(backdrop) | |
func _remove_backdrop(backdrop: String) -> void: | |
var parent = backdrop_lookup[backdrop].get_parent() | |
if parent: | |
parent.remove_child(backdrop_lookup[backdrop]) | |
else: | |
push_warning('The backdrop "%s" is not currently used' % backdrop) | |
State.forget_backdrop(backdrop) | |
func _clear_backdrop() -> void: | |
for child in BackgroundLayer.get_children(): | |
BackgroundLayer.remove_child(child) | |
State.forget_all_backdrops() | |
func _clear_characters() -> void: | |
for child in CharacterLayer.get_children(): | |
CharacterLayer.remove_child(child) | |
State.forget_all_characters() | |
func _say(string: String, pos: int = -1, rate = 1.0 / 60.0) -> void: | |
string = string.dedent().strip_edges() | |
var count = len(string) | |
var duration = rate * len(string) | |
State.remember_current_dialogue(string, pos, rate) | |
_show_dialogue() | |
match pos: | |
POS_LEFT: | |
position_dialog_left() | |
POS_CENTER: | |
position_dialog_center() | |
POS_RIGHT: | |
position_dialog_right() | |
POS_TOP: | |
position_dialog_top() | |
POS_BOTTOM: | |
position_dialog_bottom() | |
DialogueLabel.text = string | |
DialogueLabel.visible_characters = 0 | |
DialogueShower.interpolate_property(DialogueLabel, "visible_characters", 0, count, duration) | |
DialogueShower.start() | |
yield(DialogueShower, "tween_all_completed") | |
emit_signal("dialogue_shown") | |
func _respond(message: String) -> void: | |
_add_option(message) | |
yield(self, "option_pressed") | |
_hide_dialogue() | |
func _next() -> void: | |
_add_option(">> Next") | |
yield(self, "option_pressed") | |
_hide_dialogue() | |
func _wait(delay: float) -> void: | |
yield(get_tree().create_timer(delay), 'timeout') | |
func _change_stat(stat: String, delta: int) -> void: | |
State.change_stat(stat, delta) | |
func _set_stat(stat: String, value: int) -> void: | |
State.set_stat(stat, value) | |
func _when_stat(conds: Array) -> void: | |
for i in range(0, len(conds) - (len(conds) % 4), 4): | |
var stat: String = conds[i] | |
var cond: String = conds[i + 1] | |
var value: int = conds[i + 2] | |
var method: String = conds[i + 3] | |
var success: bool = false | |
match cond: | |
'<': | |
success = State.get_stat(stat) < value | |
'>': | |
success = State.get_stat(stat) > value | |
'<=': | |
success = State.get_stat(stat) <= value | |
'>=': | |
success = State.get_stat(stat) >= value | |
'=', '==': | |
success = State.get_stat(stat) == value | |
if success: | |
execute(method) | |
return | |
if len(conds) % 4 == 1: | |
execute(conds[-1]) | |
elif len(conds) % 4 == 0: | |
push_warning("Condition array is missing default clause.") | |
else: | |
push_warning("Condition array contains incomplete entries.") | |
func _change_music(track: String) -> void: | |
State.change_music(track) | |
func _show_blackout() -> void: | |
Blackout.show() | |
State.remember_blackout_visible(true) | |
func _hide_blackout() -> void: | |
Blackout.hide() | |
State.remember_blackout_visible(false) | |
func _transition(to: String) -> void: | |
yield(_fade_out(), "completed") | |
_clear_backdrop() | |
_clear_characters() | |
_clear_dialogue() | |
_clear_options() | |
_hide_dialogue() | |
_hide_blackout() | |
State.set_current_event(to) | |
func _end() -> void: | |
yield(_fade_out(), "completed") | |
_clear_backdrop() | |
_clear_characters() | |
_clear_dialogue() | |
_clear_options() | |
_hide_dialogue() | |
_hide_blackout() | |
_change_music(Music.BACKGROUND_THEME) | |
yield(_fade_in(), "completed") | |
State.enable_saving() | |
State.set_current_event(Events.NONE) | |
func position_dialog_top() -> void: | |
DialogueContainer.size_flags_horizontal = Control.SIZE_FILL | |
DialogueContainer.size_flags_vertical = 0 | |
DialogueLabel.rect_min_size = Vector2(650, 0) | |
func position_dialog_bottom() -> void: | |
DialogueContainer.size_flags_horizontal = Control.SIZE_FILL | |
DialogueContainer.size_flags_vertical = 0 | |
DialogueLabel.rect_min_size = Vector2(650, 0) | |
func position_dialog_left() -> void: | |
DialogueContainer.size_flags_horizontal = 0 | |
DialogueContainer.size_flags_vertical = Control.SIZE_FILL | |
DialogueLabel.rect_min_size = Vector2(250, 0) | |
func position_dialog_center() -> void: | |
DialogueContainer.size_flags_horizontal = Control.SIZE_SHRINK_CENTER | |
DialogueContainer.size_flags_vertical = Control.SIZE_FILL | |
DialogueLabel.rect_min_size = Vector2(250, 0) | |
func position_dialog_right() -> void: | |
DialogueContainer.size_flags_horizontal = Control.SIZE_SHRINK_END | |
DialogueContainer.size_flags_vertical = Control.SIZE_FILL | |
DialogueLabel.rect_min_size = Vector2(250, 0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment