initial commit.

This commit is contained in:
Robin Hübner 2018-09-21 03:43:57 +02:00
commit 2ae288a3d6
22 changed files with 2177 additions and 0 deletions

17
.gitattributes vendored Normal file
View File

@ -0,0 +1,17 @@
*.jpg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.ase filter=lfs diff=lfs merge=lfs -text
*.atex filter=lfs diff=lfs merge=lfs -text
*.tex filter=lfs diff=lfs merge=lfs -text
*.vs filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.pdn filter=lfs diff=lfs merge=lfs -text
*.aseprite filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.tga filter=lfs diff=lfs merge=lfs -text
*.scn filter=lfs diff=lfs merge=lfs -text
*.res filter=lfs diff=lfs merge=lfs -text

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.import
.cache
*.*.s??

55
Game.gd Normal file
View File

@ -0,0 +1,55 @@
extends Node
# globals, for the game
var reading_speed = 1.0
var game_started = false setget _on_game_started_set
var current_registry
# music related
onready var music_player = get_node("music_player")
onready var sfx_player = get_node("sfx_player")
signal on_music_volume_changed(new_value)
signal on_sfx_volume_changed(new_value)
signal on_reading_speed_changed(new_value)
signal on_settings_enter()
signal on_settings_exit()
const Themes = {}
# voices for characters
const brain_voice = null
const arborator_voice = null
const Characters = {
BRAIN = {
name = "~brain~",
voice = brain_voice,
talking_speed = 1.0,
},
ARBORATOR = {
name = "arborator",
voice = arborator_voice,
talking_speed = 1.0,
}
}
func _register_functions():
pass
func _on_game_started_set(v):
if v and not game_started:
_on_game_start()
game_started = v
func _on_game_start():
# all game state here
current_registry = {}
func _ready():
pass

23
default_bus_layout.tres Normal file
View File

@ -0,0 +1,23 @@
[gd_resource type="AudioBusLayout" format=2]
[resource]
bus/0/name = "Master"
bus/0/solo = false
bus/0/mute = false
bus/0/bypass_fx = false
bus/0/volume_db = 0.0
bus/0/send = ""
bus/1/name = "Music"
bus/1/solo = false
bus/1/mute = false
bus/1/bypass_fx = false
bus/1/volume_db = 0.0
bus/1/send = "Master"
bus/2/name = "Sfx"
bus/2/solo = false
bus/2/mute = false
bus/2/bypass_fx = false
bus/2/volume_db = 0.0
bus/2/send = "Master"

101
default_env.tres Normal file
View File

@ -0,0 +1,101 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
radiance_size = 4
sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 )
sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 )
sky_curve = 0.25
sky_energy = 1.0
ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 )
ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 )
ground_curve = 0.01
ground_energy = 1.0
sun_color = Color( 1, 1, 1, 1 )
sun_latitude = 35.0
sun_longitude = 0.0
sun_angle_min = 1.0
sun_angle_max = 100.0
sun_curve = 0.05
sun_energy = 16.0
texture_size = 2
[resource]
background_mode = 2
background_sky = SubResource( 1 )
background_sky_custom_fov = 0.0
background_color = Color( 0, 0, 0, 1 )
background_energy = 1.0
background_canvas_max_layer = 0
ambient_light_color = Color( 0, 0, 0, 1 )
ambient_light_energy = 1.0
ambient_light_sky_contribution = 1.0
fog_enabled = false
fog_color = Color( 0.5, 0.6, 0.7, 1 )
fog_sun_color = Color( 1, 0.9, 0.7, 1 )
fog_sun_amount = 0.0
fog_depth_enabled = true
fog_depth_begin = 10.0
fog_depth_curve = 1.0
fog_transmit_enabled = false
fog_transmit_curve = 1.0
fog_height_enabled = false
fog_height_min = 0.0
fog_height_max = 100.0
fog_height_curve = 1.0
tonemap_mode = 0
tonemap_exposure = 1.0
tonemap_white = 1.0
auto_exposure_enabled = false
auto_exposure_scale = 0.4
auto_exposure_min_luma = 0.05
auto_exposure_max_luma = 8.0
auto_exposure_speed = 0.5
ss_reflections_enabled = false
ss_reflections_max_steps = 64
ss_reflections_fade_in = 0.15
ss_reflections_fade_out = 2.0
ss_reflections_depth_tolerance = 0.2
ss_reflections_roughness = true
ssao_enabled = false
ssao_radius = 1.0
ssao_intensity = 1.0
ssao_radius2 = 0.0
ssao_intensity2 = 1.0
ssao_bias = 0.01
ssao_light_affect = 0.0
ssao_color = Color( 0, 0, 0, 1 )
ssao_quality = 0
ssao_blur = 3
ssao_edge_sharpness = 4.0
dof_blur_far_enabled = false
dof_blur_far_distance = 10.0
dof_blur_far_transition = 5.0
dof_blur_far_amount = 0.1
dof_blur_far_quality = 1
dof_blur_near_enabled = false
dof_blur_near_distance = 2.0
dof_blur_near_transition = 1.0
dof_blur_near_amount = 0.1
dof_blur_near_quality = 1
glow_enabled = false
glow_levels/1 = false
glow_levels/2 = false
glow_levels/3 = true
glow_levels/4 = false
glow_levels/5 = true
glow_levels/6 = false
glow_levels/7 = false
glow_intensity = 0.8
glow_strength = 1.0
glow_bloom = 0.0
glow_blend_mode = 2
glow_hdr_threshold = 1.0
glow_hdr_scale = 2.0
glow_bicubic_upscale = false
adjustment_enabled = false
adjustment_brightness = 1.0
adjustment_contrast = 1.0
adjustment_saturation = 1.0

450
dialog.gd Normal file
View File

@ -0,0 +1,450 @@
extends Control
# load from dialog vm
const Dialog = preload("res://dialog_vm.gd")
const DialogController = preload("res://dialog_controller.gd")
# classes from there
const DialogSegment = Dialog.DialogSegment
const DialogWithSubjectSegment = Dialog.DialogWithSubjectSegment
const DialogWithSubjectExpressionSegment = Dialog.DialogWithSubjectExpressionSegment
const QuestionSegment = Dialog.QuestionSegment
const ChoiceSegment = Dialog.ChoiceSegment
const ChoiceWithJump = Dialog.ChoiceWithJump
const Choice = Dialog.Choice
const WaitSegment = Dialog.YieldCallSegment
# resources specific to this dialogy thingy
const DialogChoices = preload("res://ui/dialog_choices.tscn")
const DialogChoice = preload("res://ui/dialog_choice.tscn")
var IS_DEBUG = false
var current_dialog
var current_dialog_controller
# ui elements
onready var advance_button = get_node("top_panel/auto_btn")
onready var fastforward_button = get_node("top_panel/ffwd_btn")
onready var title_label = get_node("body_container/bottom_panel/subject_margin/subject_panel/subject_label")
onready var body_container = get_node("body_container")
onready var body_label = get_node("body_container/bottom_panel/body_margin/body_label")
# clickable regions, for accessibility
onready var top_panel = get_node("top_panel")
onready var audio_player = get_node("audio_player")
# params to set from outside
var dialog_path
# activeness
var active
# tween to fade in/out
var tween
# timer
var timer
# anim player, if any
var anim_player
# linkin in ur get_node relative to where you want
var get_node_fnc
# various main dialog related constants
# signals
signal entered_dialog
signal exited_frame
# character related signals
signal on_speaker_expression_changed(spkr, expr)
signal on_dialog_init(d)
signal on_dialog_completed(d)
# setters
func set_auto_advance(v):
current_dialog_controller.set_advance_state(v)
advance_button.pressed = v
# signals from dialog controller
func _on_printing_changed(v):
pass
# next_arrow.visible = v
func _on_reading_speed_changed(new_value):
current_dialog_controller.set_reading_speed(new_value)
func set_advance_state():
current_dialog_controller.set_advance_state(advance_button.is_pressed())
func set_fastforward_state():
var ffwd_pressed = fastforward_button.is_pressed()
current_dialog_controller.set_fastforward_state(fastforward_button.is_pressed())
advance_button.set_disabled(ffwd_pressed)
# init functions, must be called before use
func set_dialog_path(path):
dialog_path = path
func set_dialog_vm(vm):
current_dialog = vm
func set_dialog_controller(c):
current_dialog_controller = c
func set_dialog_anim_player(p):
anim_player = p
func set_get_character_function(fnc):
get_node_fnc = fnc
func _ready():
advance_button.connect("pressed", self, "set_advance_state")
fastforward_button.connect("pressed", self, "set_fastforward_state")
# clicking to advance
body_container.connect("gui_input", self, "_on_body_click")
top_panel.connect("gui_input", self, "_on_body_click")
Game.connect("on_reading_speed_changed", self, "_on_reading_speed_changed")
Game.connect("on_settings_enter", self, "_on_settings_enter")
Game.connect("on_settings_exit", self, "_on_settings_exit")
tween = Tween.new()
add_child(tween)
timer = Timer.new()
add_child(timer)
func _on_body_click(ev):
current_dialog_controller._on_body_click(is_active(), ev)
func _register_func(r, reg_name, func_name):
if r.has(reg_name):
r[reg_name].set_instance(self)
else:
r[reg_name] = funcref(self, func_name)
func _register_functions():
var r = current_dialog.get_local_registry()
# normal functions
_register_func(r, "SET_EXPR", "_set_expr_fnc")
_register_func(r, "FLIP", "_flip_char")
_register_func(r, "HIDE", "_hide_char")
_register_func(r, "SHOW", "_show_char")
# yieldy function
_register_func(r, "WAIT", "_wait_dialog_fnc")
# anim shite, also yieldy
_register_func(r, "PLAY_ANIM", "_play_anim_fnc")
# PLAY AND WAIT; HOW ABOUT DEM APPLES
_register_func(r, "PLAY_ANIM_WITH_WAIT", "_play_wait_with_anim_fnc")
func _play_wait_with_anim_fnc(args):
if args.size() == 2:
var anim_name = args[0]
var wait_time = float(args[1])
if anim_player:
anim_player.play(anim_name)
timer.one_shot = true
timer.wait_time = wait_time
timer.start()
return [timer, "timeout"]
func _play_anim_fnc(args):
if args.size() == 1:
var anim_name = args[0]
if anim_player:
anim_player.play(anim_name)
return [anim_player, "animation_finished"]
func _wait_dialog_fnc(args):
if args.size() == 1:
var time = float(args[0])
print("Dialog: waiting for: %f seconds" % time)
timer.wait_time = time
timer.one_shot = true
timer.start()
return [timer, "timeout"]
func _set_expr_fnc(args):
if args.size() == 2:
var speaker = args[0]
var expr = args[1]
_set_speaker_expression(speaker, expr)
func _show_char(args):
var char_name = args[0]
var character = get_node_fnc.call(char_name)
_do_show_char(character)
func _do_show_char(c):
tween.interpolate_property(
c, "modulate:a", c.modulate.a, 1.0,
1.0, Tween.TRANS_QUAD,
Tween.EASE_IN_OUT
)
tween.start()
func _hide_char(args):
var char_name = args[0]
var character = get_node_fnc.call(char_name)
_do_hide_char(character)
func _do_hide_char(c):
tween.interpolate_property(
c, "modulate:a", c.modulate.a, 0.0,
1.0, Tween.TRANS_QUAD,
Tween.EASE_IN_OUT
)
tween.start()
func _flip_char(args):
var char_name = args[0]
var character = get_node_fnc.call(char_name)
_do_flip_char(character)
func _do_flip_char(c):
c.flip_h = !c.flip_h
func _on_settings_enter(s):
if is_active():
visible = false
func _on_settings_exit(s):
if is_active():
visible = true
func reload_dialog():
initialize_dialog()
func get_local_registry():
return current_dialog.get_local_registry()
func initialize_dialog():
assert(dialog_path != null)
var dialog_file = File.new()
dialog_file.open(dialog_path, File.READ)
var text = dialog_file.get_as_text()
dialog_file.close()
var dialog = Dialog.dialog_from_text(text)
current_dialog = dialog
# also setup kv store for dialog variables
current_dialog.set_registry(Game.current_registry)
# set up controller too yes
current_dialog_controller = DialogController.new(dialog, audio_player)
# register functions
_register_functions()
# signals
current_dialog_controller.connect("on_update_dialog", self, "_on_update_dialog")
current_dialog_controller.connect("on_dialog_completed", self, "_on_dialog_completed")
current_dialog_controller.connect("on_printing_changed", self, "_on_printing_changed")
emit_signal("on_dialog_init", self)
func _on_dialog_completed(d):
emit_signal("on_dialog_completed", self)
exit_frame()
func _on_update_dialog(s):
_update_dialog(s)
func _fade_in():
var FADE_TIME = 1.0
tween.interpolate_property(
self,
"modulate:a", 0.0, 1.0, FADE_TIME,
Tween.TRANS_QUAD, Tween.EASE_IN
)
tween.start()
func _fade_out():
var FADE_TIME = 1.0
tween.interpolate_property(
self,
"modulate:a", 1.0, 0.0, FADE_TIME,
Tween.TRANS_QUAD, Tween.EASE_OUT
)
tween.start()
yield(tween, "tween_completed")
visible = false
func enter_frame():
_fade_in()
var r = Game.current_registry
r["IS_IN_DIALOG"] = true
# reload if is debug
if IS_DEBUG: reload_dialog()
active = true
visible = true
current_dialog.reset()
# next_arrow.visible = false
emit_signal("entered_dialog")
# dialogs
var first_segment = current_dialog.advance()
_update_dialog(first_segment)
func is_active():
return active
func exit_frame():
_fade_out()
# HACK: cludge to reset state
advance_button.set_pressed(false)
fastforward_button.set_pressed(false)
advance_button.emit_signal("pressed")
fastforward_button.emit_signal("pressed")
current_dialog_controller.reset()
active = false
var r = Game.current_registry
r["IS_IN_DIALOG"] = false
emit_signal("exited_frame")
func _set_speaker_subject(subject):
if Game.Characters.has(subject):
var data = Game.Characters[subject]
var subject_name = data.name
var subject_colour = data.name_colour
var subject_voice = data.voice
current_dialog_controller.chars_per_second = Game.DEFAULT_CHARS_PER_SECOND * data.talking_speed
_set_speaker_name(subject_name, subject_colour)
if subject_voice != null:
_set_speaker_voice(subject_voice)
else:
var subject_name = subject
current_dialog_controller.chars_per_second = Game.DEFAULT_CHARS_PER_SECOND
_set_speaker_name(subject_name, Color(1, 1, 1))
func _get_speaker_name():
return title_label.text
func _set_speaker_name(speaker_name, colour = Color(1, 1, 1)):
title_label.set_text(speaker_name)
title_label.modulate = colour
func _set_speaker_voice(voice_sample):
audio_player.stream = voice_sample
func _set_speaker_expression(speaker, expression):
emit_signal("on_speaker_expression_changed", speaker, expression)
# temporary maybe
func _setup_question(new_question):
body_container.add_child(new_question)
current_dialog_controller.waiting_for_answer = true
func _question_answered_yes(qs):
body_container.remove_child(qs)
current_dialog_controller.waiting_for_answer = false
current_dialog_controller._advance_dialog()
func _question_answered_no(qs):
body_container.remove_child(qs)
current_dialog_controller.waiting_for_answer = false
exit_frame()
func _choice_made(choice_modal, choice):
if IS_DEBUG: print("chose: ", choice.get_choice())
# Game.add_history_entry("you", choice.get_choice())
remove_child(choice_modal)
if choice is ChoiceWithJump:
# HACK: fix this later or make this unnecessary
var current_segment = current_dialog.jump(choice.target)
call_deferred("_update_dialog", current_segment)
else:
_update_dialog(choice)
current_dialog_controller.waiting_for_answer = false
# loop de loop
func _update_dialog(segment):
if segment is ChoiceSegment:
var choices = segment.get_choices()
# create new dialog choices and shit
var new_choices = DialogChoices.instance()
var vbox = new_choices.get_node("vbox")
for choice in choices:
var new_choice = DialogChoice.instance()
new_choice.text = choice.get_choice()
new_choice.connect("pressed", self, "_choice_made", [new_choices, choice])
vbox.add_child(new_choice)
add_child(new_choices)
elif segment is Choice:
var new_content = segment.get_response()
if typeof(new_content) == TYPE_OBJECT and (new_content is DialogWithSubjectSegment or new_content is DialogWithSubjectExpressionSegment):
_update_dialog(new_content)
else:
body_label.text = new_content
# Game.add_history_entry(_get_speaker_name(), new_content)
elif segment is DialogSegment or segment is DialogWithSubjectSegment or segment is DialogWithSubjectExpressionSegment:
var new_content = segment.get_content()
body_label.visible_characters = 0
body_label.text = new_content
if segment is DialogWithSubjectSegment:
_set_speaker_subject(segment.subject)
# Game.add_history_entry(_get_speaker_name(), new_content)
elif segment is DialogWithSubjectExpressionSegment:
_set_speaker_subject(segment.subject)
_set_speaker_expression(segment.subject, segment.expression)
# Game.add_history_entry("%s (%s)" % [_get_speaker_name(), segment.expression], new_content)
else:
pass
# Game.add_history_entry(_get_speaker_name(), new_content)
elif segment is WaitSegment:
var new_content = segment.get_content()
body_label.visible_characters = 0
body_label.text = new_content
# whatever subject you want during functions to wait on, animations and such
_set_speaker_subject("")
else:
print("encountered unknown segment: " + segment.get_content())
current_dialog_controller.update_logic_dialog(segment)
func _process(delta):
if is_active():
current_dialog_controller.tick(delta, body_label)

2
dialog/introduction.txt Normal file
View File

@ -0,0 +1,2 @@
<BRAIN> ... You wake up to the sound of sirens blaring.

173
dialog_controller.gd Normal file
View File

@ -0,0 +1,173 @@
extends Reference
const Dialog = preload("res://dialog_vm.gd")
# classes from vm
const DialogSegment = Dialog.DialogSegment
const DialogWithSubjectSegment = Dialog.DialogWithSubjectSegment
const DialogWithSubjectExpressionSegment = Dialog.DialogWithSubjectExpressionSegment
const QuestionSegment = Dialog.QuestionSegment
const ChoiceSegment = Dialog.ChoiceSegment
const ChoiceWithJump = Dialog.ChoiceWithJump
const Choice = Dialog.Choice
const WaitSegment = Dialog.YieldCallSegment
# current dialog vm
var current_dialog
var audio_player
# dialog controller related state, internal
# speed at which dialog prints characters
var chars_per_second = 16 # initialized from which character is speaking
var reading_speed_mul = 1
var special_speed_mul = 1
# intermediate state for dialog
var waiting_for_answer = false
var print_done = false setget _on_print_set
var print_target = 0
var print_delta = 0
# ui button controlled state
var is_auto_advancing = false
var auto_advance_delay = 1
var auto_advance_timer = 0
var is_new_word = false
var is_fastforwarding = false
var ff_speed_mul = 1
# character printing sound related state
var sound_playing = false
var sound_play_time = 2.08 # TODO: maybe adjust this too depending on voice sample/chars per second
var sound_played_time = 0
# signals
signal on_printing_done()
signal on_printing_changed(status)
signal on_update_dialog(segment)
signal on_dialog_completed(d)
func _init(d, a):
current_dialog = d
audio_player = a
func reset():
self.print_done = true
print_target = 0
print_delta = 0
# sfx related
func sound_play():
sound_playing = true
audio_player.play()
func sound_stop():
audio_player.stop()
sound_playing = false
func sound_done_playing(delta):
sound_played_time += delta * reading_speed_mul * special_speed_mul
if sound_played_time >= sound_play_time / chars_per_second:
sound_played_time = 0
sound_playing = false
return not sound_playing
# temporary hopefully
func _advance_dialog():
var current_segment = current_dialog.advance()
if not current_segment == null:
emit_signal("on_update_dialog", current_segment)
else:
emit_signal("on_dialog_completed", self)
func _on_body_click(is_visible, event):
if is_visible and not waiting_for_answer:
if event.is_action_pressed("dialog_click"):
if print_done == false: # if still printing, jump forwards now
print_delta = print_target - 1
self.print_done = true
else:
_advance_dialog()
# interior callbacks
func _on_print_set(v):
print_done = v
if print_done:
emit_signal("on_printing_done")
emit_signal("on_printing_changed", v)
func set_reading_speed(spd):
reading_speed_mul = spd
func set_advance_state(s):
is_auto_advancing = s
func set_fastforward_state(s):
is_fastforwarding = s
if is_fastforwarding:
is_auto_advancing = true
ff_speed_mul = 10
auto_advance_delay = 0.1
else:
ff_speed_mul = 1
auto_advance_delay = 1
func update_logic_dialog(segment):
if segment is QuestionSegment:
var new_content = segment.get_content()
print_target = new_content.length()
self.print_done = false
print_delta = 0
elif segment is ChoiceSegment:
waiting_for_answer = true
elif segment is Choice:
var new_content = segment.get_response()
if typeof(new_content) == TYPE_OBJECT and (new_content is DialogWithSubjectSegment or new_content is DialogWithSubjectExpressionSegment):
_update_logic_dialog(new_content)
else:
print_target = new_content.length()
self.print_done = false
print_delta = 0
elif segment is DialogSegment or segment is DialogWithSubjectSegment or segment is DialogWithSubjectExpressionSegment:
var new_content = segment.get_content()
print_target = new_content.length()
self.print_done = false
print_delta = 0
elif segment is WaitSegment:
var new_content = segment.get_content()
print_target = new_content.length()
self.print_done = false
print_delta = 0
else:
print("encountered unknown segment: " + segment.get_content())
func tick(delta, body_label):
if not current_dialog.is_eof():
var cur_segment = current_dialog.segments[current_dialog.segment_index]
if cur_segment is WaitSegment and cur_segment.finished: _advance_dialog()
if print_target != 0:
if print_delta >= print_target:
self.print_done = true
auto_advance_timer = auto_advance_delay
print_target = 0
print_delta = 0
else:
if sound_done_playing(delta) and not is_fastforwarding: sound_play()
body_label.visible_characters = print_delta
print_delta += delta * (chars_per_second * reading_speed_mul) * ff_speed_mul
elif print_target == 0:
auto_advance_timer -= delta
if auto_advance_timer <= 0 and is_auto_advancing and not waiting_for_answer:
call_deferred("_advance_dialog")

29
dialog_layer.gd Normal file
View File

@ -0,0 +1,29 @@
extends CanvasLayer
export (String, FILE, "*.txt") var dialog_path;
onready var dialog_box = get_node("dialog_box")
func _ready():
# start signal
get_parent().connect("on_scene_ready", self, "_on_scene_ready")
dialog_box.connect("on_dialog_init", self, "_on_dialog_init")
dialog_box.connect("on_dialog_completed", self, "_on_dialog_completed")
# set up params
dialog_box.set_dialog_path(dialog_path)
# init after yes
dialog_box.initialize_dialog()
func _on_scene_ready():
dialog_box.enter_frame()
func _on_dialog_init(d):
pass
func _on_dialog_completed(d):
dialog_box.exit_frame()

570
dialog_vm.gd Normal file
View File

@ -0,0 +1,570 @@
extends Reference
static func dialog_from_text(text):
var label_regex = RegEx.new()
label_regex.compile("[A-Z0-9_]+:")
# validation
var has_question = false
var lines = text.split("\n")
var new_dialog = Dialog.new()
# intermediate state
var current_choice_segment
var choices = 0
if current_choice_segment == null:
current_choice_segment = ChoiceSegment.new()
var line_number = 0
for line in lines:
line_number += 1
# comment lines
if line.begins_with("#"):
pass
# is flag unset
elif line.begins_with("!@"):
var l = line.right(3).strip_edges()
var flag = l.left(l.length() - 1)
# if OS.is_debug_build(): print("flag to false: ", flag)
new_dialog.add_segment(FlagSetSegment.new(flag, false))
# is goto, or conditional goto
elif line.begins_with("!"):
var label_end = line.find(",")
# unconditional goto
if label_end == -1:
var label = line.substr(2, line.length() - 3).strip_edges()
# if OS.is_debug_build(): print("goto: ", label)
new_dialog.add_segment(GotoSegment.new(label))
else: # conditional goto
var label = line.substr(2, label_end - 2).strip_edges()
var target_end = line.find("]")
var target = line.substr(label_end + 1, (target_end - 1) - label_end).strip_edges()
# if OS.is_debug_build(): print("goto: ", target, " - ", label)
new_dialog.add_segment(ConditionalGotoSegment.new(target, label, true))
# is either random goto or question
elif line.begins_with("?"):
var str_line = line.strip_edges().right(1)
# question, initiates dialog, treat first encountered question as question
if str_line.begins_with("<"):
var q_line = str_line.strip_edges()
has_question = true
var subject_right_bracket_index = q_line.find(">")
var subject = q_line.substr(1, subject_right_bracket_index - 1).strip_edges()
var question_text = q_line.right(subject_right_bracket_index + 1).strip_edges()
var question_segment = QuestionSegment.new(question_text, subject)
new_dialog.add_segment(question_segment)
else: # random goto: picks one of the choices to goto, used like ?[P1, P2, P3]
var left_bracket = str_line.find("[")
var right_bracket = str_line.find("]")
if left_bracket == -1:
printerr("couldn't find left [ on line: %d" % line_number)
assert(false)
if right_bracket == -1:
printerr("couldn't find right ] on line: %d" % line_number)
assert(false)
# if there's actually even a single thing inside
if right_bracket - left_bracket > 1:
var arg_string = str_line.substr(left_bracket + 1, (right_bracket - left_bracket) - 1)
var args = arg_string.split(",", false)
var stripped_args = []
for arg in args:
stripped_args.push_back(arg.strip_edges())
# if OS.is_debug_build(): print("random goto, arg_string: %s" % arg_string)
new_dialog.add_segment(RandomGotoSegment.new(stripped_args))
# is flag set, either a normal set to true with @[SOME_FLAG] or a set to string with @[FLAG, STR]
elif line.begins_with("@"):
var l = line.right(2)
var comma_index = l.find(",")
if comma_index == -1:
var flag = l.left(l.length() - 1)
# if OS.is_debug_build(): print("flag to true: ", flag)
new_dialog.add_segment(FlagSetSegment.new(flag, true))
else:
var flag = l.left(comma_index).strip_edges()
var arg_end = l.find("]")
var arg_int = l.right(comma_index + 1)
var arg = arg_int.left(arg_int.length() - 1).strip_edges()
# if OS.is_debug_build(): print("flag: ", flag, " with arg: ", arg)
new_dialog.add_segment(FlagSetSegment.new(flag, arg))
# is label, can goto
elif line.ends_with(":") and label_regex.search(line):
var label = line.left(line.length() - 1).strip_edges()
# if OS.is_debug_build(): print("label: ", label)
new_dialog.add_segment(LabelSegment.new(label))
# is choice in conversation by player
elif line.begins_with("["):
var right_end_index = line.find("]")
var choice = line.substr(1, right_end_index - 1).strip_edges()
var response = line.right(right_end_index + 1).strip_edges()
# is it a jump choice?
var left_brace_index = response.find("[")
var right_brace_index = response.find("]")
if left_brace_index != -1 and right_brace_index != -1:
var jump_target = response.substr(left_brace_index + 1, right_brace_index - 1)
# if OS.is_debug_build(): print("choice: ", choice, " target: ", jump_target)
current_choice_segment.add_choice_with_jump(choice, jump_target)
else:
var subject_right_comma_index = response.find(",")
var subject_right_bracket_index = response.find(">")
# is response with <SUBJECT, EXPR>
if response.begins_with("<") and subject_right_bracket_index > 0 and subject_right_comma_index < subject_right_bracket_index:
var subject = response.substr(1, subject_right_comma_index - 1).strip_edges()
var expression = response.substr(subject_right_comma_index + 1, (subject_right_bracket_index - 1) - subject_right_comma_index).strip_edges()
var line_text = response.right(subject_right_bracket_index + 1).strip_edges()
# if OS.is_debug_build(): print("choice: ", choice, " response: ", line_text, " with: <", subject, ", ", expression, ">")
current_choice_segment.add_choice(choice, DialogWithSubjectExpressionSegment.new(line_text, subject, expression))
elif response.begins_with("<"): # is response with <SUBJECT>
var subject = response.substr(1, subject_right_bracket_index - 1).strip_edges()
var line_text = response.right(subject_right_bracket_index + 1).strip_edges()
# if OS.is_debug_build(): print("choice: ", choice, " response: ", line_text, " with: <", subject, ">")
current_choice_segment.add_choice(choice, DialogWithSubjectSegment.new(line_text, subject))
else:
# if OS.is_debug_build(): print("choice: ", choice, " response: ", response)
current_choice_segment.add_choice(choice, response)
choices += 1
# we're full
if choices == 3:
# if OS.is_debug_build(): print("choice segment created.")
new_dialog.add_segment(current_choice_segment)
# setup new one for next round
current_choice_segment = ChoiceSegment.new()
choices = 0
elif line.begins_with("<"):
# test for right comma and >
var subject_right_comma_index = line.find(",")
var subject_right_bracket_index = line.find(">")
# is <SUBJECT, EXPRESSION> dialog
if subject_right_comma_index > 0 and subject_right_comma_index < subject_right_bracket_index:
var subject = line.substr(1, subject_right_comma_index - 1).strip_edges()
var expression = line.substr(subject_right_comma_index + 1, (subject_right_bracket_index - 1) - subject_right_comma_index).strip_edges()
var line_text = line.right(subject_right_bracket_index + 1).strip_edges()
var dialog_subject_segment = DialogWithSubjectExpressionSegment.new(line_text, subject, expression)
new_dialog.add_segment(dialog_subject_segment)
else: # is <SUBJECT> dialog
var subject = line.substr(1, subject_right_bracket_index - 1).strip_edges()
var line_text = line.right(subject_right_bracket_index + 1).strip_edges()
var dialog_subject_segment = DialogWithSubjectSegment.new(line_text, subject)
new_dialog.add_segment(dialog_subject_segment)
# simple function calls, can look like: > FUNC, but also like > FUNC [A, B, C, D]
# yieldable function calls can look like: >> FUNC, but also like >> FUNC [A, B, C, D]
elif line.begins_with(">"):
var str_line = line.right(1).strip_edges()
var is_yielding_function = false
if str_line.begins_with(">"):
str_line = str_line.right(1).strip_edges()
is_yielding_function = true
var left_bracket = str_line.find("[")
var right_bracket = str_line.find("]")
# has left bracket, should have right bracket, has params
if (left_bracket != -1 and right_bracket != -1) or \
(left_bracket != -1 and right_bracket == -1) or \
(left_bracket == -1 and right_bracket != -1):
var command_end = str_line.find("[")
var command = str_line.left(command_end).strip_edges()
# if OS.is_debug_build(): print("parsing command with args: ", command)
if left_bracket == -1:
printerr("couldn't find left [ on line: %d" % line_number)
assert(false)
if right_bracket == -1:
printerr("couldn't find right ] on line: %d" % line_number)
assert(false)
var args_str = str_line.substr(left_bracket + 1, (right_bracket - left_bracket) - 1).strip_edges()
var args = args_str.split(",", false)
var stripped_args = []
for a in args:
stripped_args.push_back(a.strip_edges())
if is_yielding_function:
var call_segment = YieldCallSegment.new(command, stripped_args)
new_dialog.add_segment(call_segment)
else:
var call_segment = CallSegment.new(command, stripped_args)
new_dialog.add_segment(call_segment)
else: # no params, argless call
var command = str_line.strip_edges()
# if OS.is_debug_build(): print("parsing command: ", command)
if is_yielding_function:
var call_segment = YieldCallSegment.new(command)
new_dialog.add_segment(call_segment)
else:
var call_segment = CallSegment.new(command)
new_dialog.add_segment(call_segment)
else: # is normal text
var line_text = line.strip_edges()
# if line is not empty, process
if not line_text.empty():
var dialog_segment = DialogSegment.new(line_text)
new_dialog.add_segment(dialog_segment)
return new_dialog
static func parse_dialog_line(text):
pass
# types of segments of text, held in Dialogs array
class QuestionSegment:
var subject
var question
func _init(q, s):
question = q
subject = s
func get_content():
return question
class DialogSegment:
var text
func _init(t):
text = t
func get_content():
return text
class DialogWithSubjectSegment:
var text
var subject
func _init(t, s):
text = t
subject = s
func get_content():
return text
class DialogWithSubjectExpressionSegment:
var text
var subject
var expression
func _init(t, s, e):
text = t
subject = s
expression = e
func get_content():
return text
class Choice:
var choice
var response
func _init(c, r):
choice = c
response = r
func get_choice():
return choice
func get_response():
return response
class ChoiceWithJump:
var choice
var target
func _init(c, t):
choice = c
target = t
func get_choice():
return choice
func get_target():
return target
class ChoiceSegment:
var choices
func _init():
choices = []
func add_choice(choice, response):
var new_choice = Choice.new(choice, response)
choices.push_back(new_choice)
func add_choice_with_jump(choice, target):
var new_choice = ChoiceWithJump.new(choice, target)
choices.push_back(new_choice)
func get_choices():
return choices
class LabelSegment:
var label
func _init(l):
label = l
class GotoSegment:
var target
func _init(t):
target = t
class RandomGotoSegment:
var targets
func _init(t):
targets = t
class ConditionalGotoSegment extends GotoSegment:
var variable
var expected_value
func _init(t, v, e).(t):
variable = v
expected_value = e
func test(registry):
if variable in registry:
var v = registry[variable]
return v
else:
return false
class CallSegment:
var fname
var args
func _init(f, a = null):
fname = f
args = a
func call(l, r):
if l.has(fname):
if args != null: return l[fname].call_func(args)
else: return l[fname].call_func([])
elif r.has(fname):
if args != null: return r[fname].call_func(args)
else: return r[fname].call_func([])
class YieldCallSegment extends CallSegment:
var started
var finished
func _init(f, a = null).(f, a):
started = false
func call(l, r):
started = true
finished = false
return .call(l, r)
func get_content():
return "..."
class FlagSetSegment:
var flag
var value
func _init(f, v):
flag = f
value = v
func set(registry):
registry[flag] = value
class Dialog:
var segment_index = -1
var segments
# goto label dictionary
var labels
# local registry
var locals
# dependency, registry of stuff
var registry
# yield state
var is_yielding
func _init():
is_yielding = false
segments = []
labels = {}
locals = {}
func get_local_registry():
return locals
func set_registry(r):
registry = r
func add_segment(s):
if s is GotoSegment:
if not s.target in labels:
labels[s.target] = -1
elif s is RandomGotoSegment:
for t in s.targets:
if not t in labels:
labels[t] = -1
elif s is LabelSegment:
var sz = segments.size()
if not labels.has(s.label) or labels[s.label] == -1:
# if OS.is_debug_build(): print("added label: ", s.label, " to: ", sz)
labels[s.label] = sz
else:
printerr("ERROR: label with name: %s already registered at %d!" % [s.label, labels[s.label]])
assert(labels[s.label] != -1)
segments.push_back(s)
func push_instruction():
if is_eof(): return null
var new_segment = segments[segment_index]
while (new_segment is GotoSegment \
or new_segment is RandomGotoSegment \
or new_segment is LabelSegment \
or new_segment is CallSegment \
or new_segment is FlagSetSegment) and not is_yielding and not is_eof():
new_segment = segments[segment_index]
if new_segment is ConditionalGotoSegment:
if new_segment.test(registry): # if true, jump to offset
var offset = labels[new_segment.target]
if offset == -1:
printerr("could not find label: %s" % new_segment.target)
assert(offset != -1)
# if OS.is_debug_build(): print("conditionally jumped to offset: ", offset)
segment_index = offset
else: # else skip
segment_index += 1
elif new_segment is RandomGotoSegment:
var rand_idx = floor(rand_range(0, new_segment.targets.size()))
var rand_target = new_segment.targets[rand_idx]
var offset = labels[rand_target]
if offset == -1:
printerr("could not find label: %s" % rand_target)
assert(offset != -1)
# if OS.is_debug_build(): print("random jumped to: ", offset, " with: ", rand_target)
segment_index = offset
elif new_segment is GotoSegment:
var offset = labels[new_segment.target]
if offset == -1:
printerr("could not find label: %s" % new_segment.target)
assert(offset != -1)
# if OS.is_debug_build(): print("jumped to: ", offset, " with: ", new_segment.target, " at: ", segment_index)
segment_index = offset
elif new_segment is YieldCallSegment:
if not new_segment.started:
var state = new_segment.call(locals, registry)
if state != null:
var obj = state[0]
var sgn = state[1]
is_yielding = true
_do_yield_wait(obj, sgn, new_segment)
else:
segment_index += 1
elif new_segment is CallSegment:
new_segment.call(locals, registry)
segment_index += 1
elif new_segment is FlagSetSegment:
new_segment.set(registry)
segment_index += 1
elif new_segment is LabelSegment:
segment_index += 1
if not is_eof():
return segments[segment_index]
else:
return null
func advance():
if not is_yielding:
segment_index += 1
return push_instruction()
else:
return push_instruction()
# external jump, is fudge
func jump(target):
segment_index = labels[target]
return push_instruction()
func is_eof():
return segment_index >= segments.size()
func reset():
segment_index = -1
func _do_yield_wait(obj, sgn, segment):
yield(obj, sgn)
segment.finished = true
is_yielding = false
printt("Dialog VM: %s %s %s" % [obj, sgn, "completed"])

59
game.gd Normal file
View File

@ -0,0 +1,59 @@
extends Node
# globals, for the game
var reading_speed = 1.0
var game_started = false setget _on_game_started_set
var current_registry
# music related
onready var music_player = get_node("music_player")
onready var sfx_player = get_node("sfx_player")
signal on_music_volume_changed(new_value)
signal on_sfx_volume_changed(new_value)
signal on_reading_speed_changed(new_value)
signal on_settings_enter()
signal on_settings_exit()
const Themes = {}
# voices for characters
const brain_voice = null
const arborator_voice = null
const DEFAULT_CHARS_PER_SECOND = 32
const Characters = {
BRAIN = {
name = "~brain~",
name_colour = Color(1, 1, 1, 1),
voice = brain_voice,
talking_speed = 1.0,
},
ARBORATOR = {
name = "arborator",
name_colour = Color(1, 1, 1, 1),
voice = arborator_voice,
talking_speed = 1.0,
}
}
func _register_functions():
pass
func _on_game_started_set(v):
if v and not game_started:
_on_game_start()
game_started = v
func _on_game_start():
# all game state here
current_registry = {}
func _ready():
pass

27
game.tscn Normal file
View File

@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://game.gd" type="Script" id=1]
[node name="Game" type="Node"]
script = ExtResource( 1 )
[node name="music_player" type="AudioStreamPlayer" parent="." index="0"]
stream = null
volume_db = 0.0
pitch_scale = 1.0
autoplay = false
mix_target = 0
bus = "Music"
[node name="sfx_player" type="AudioStreamPlayer" parent="." index="1"]
stream = null
volume_db = 0.0
pitch_scale = 1.0
autoplay = false
mix_target = 0
bus = "Sfx"

BIN
icon.png (Stored with Git LFS) Normal file

Binary file not shown.

29
icon.png.import Normal file
View File

@ -0,0 +1,29 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
[deps]
source_file="res://icon.png"
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

26
menus/main_menu.gd Normal file
View File

@ -0,0 +1,26 @@
extends Control
onready var start_btn = get_node("split/buttons/start_btn")
onready var load_btn = get_node("split/buttons/load_btn")
onready var options_btn = get_node("split/buttons/options_btn")
onready var quit_btn = get_node("split/buttons/quit_btn")
func _ready():
start_btn.connect("pressed", self, "_on_start_pressed")
load_btn.connect("pressed", self, "_on_load_pressed")
options_btn.connect("pressed", self, "_on_options_pressed")
quit_btn.connect("pressed", self, "_on_quit_pressed")
func _on_start_pressed():
pass
func _on_load_pressed():
pass
func _on_options_pressed():
pass
func _on_quit_pressed():
get_tree().quit()

182
menus/main_menu.tscn Normal file
View File

@ -0,0 +1,182 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://menus/main_menu.gd" type="Script" id=1]
[node name="main_menu" type="Control"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
script = ExtResource( 1 )
[node name="split" type="VSplitContainer" parent="." index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
split_offset = 0
collapsed = false
dragger_visibility = 0
[node name="title" type="Label" parent="split" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 140.0
margin_right = 1024.0
margin_bottom = 154.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 6
text = "godot-vn"
align = 1
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "Size Flags" ]
[node name="buttons" type="VBoxContainer" parent="split" index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 306.0
margin_right = 1024.0
margin_bottom = 600.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 3
alignment = 0
_sections_unfolded = [ "Size Flags" ]
[node name="start_btn" type="Button" parent="split/buttons" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 1024.0
margin_bottom = 20.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 0, 0, 0, 1 )
custom_colors/font_color_pressed = Color( 0, 0, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = "start game"
flat = true
align = 1
[node name="load_btn" type="Button" parent="split/buttons" index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 24.0
margin_right = 1024.0
margin_bottom = 44.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 0, 0, 0, 1 )
custom_colors/font_color_pressed = Color( 0, 0, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = "load game"
flat = true
align = 1
[node name="options_btn" type="Button" parent="split/buttons" index="2"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 48.0
margin_right = 1024.0
margin_bottom = 68.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 0, 0, 0, 1 )
custom_colors/font_color_pressed = Color( 0, 0, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = "options"
flat = true
align = 1
[node name="quit_btn" type="Button" parent="split/buttons" index="3"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 72.0
margin_right = 1024.0
margin_bottom = 92.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 0, 0, 0, 1 )
custom_colors/font_color_pressed = Color( 0, 0, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = "quit"
flat = true
align = 1

33
project.godot Normal file
View File

@ -0,0 +1,33 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=3
[application]
config/name="godot-vn"
run/main_scene="res://scene.tscn"
config/icon="res://icon.png"
[autoload]
SceneSwitcher="*res://scene_switcher.gd"
Game="*res://game.tscn"
[display]
window/size/resizable=false
[input]
dialog_click=[ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null)
]
[rendering]
environment/default_environment="res://default_env.tres"

7
scene.gd Normal file
View File

@ -0,0 +1,7 @@
extends Node2D
signal on_scene_ready
func _ready():
Game.game_started = true
emit_signal("on_scene_ready")

307
scene.tscn Normal file
View File

@ -0,0 +1,307 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://scene.gd" type="Script" id=1]
[ext_resource path="res://dialog_layer.gd" type="Script" id=2]
[ext_resource path="res://dialog.gd" type="Script" id=3]
[sub_resource type="StyleBoxEmpty" id=1]
content_margin_left = -1.0
content_margin_right = -1.0
content_margin_top = -1.0
content_margin_bottom = -1.0
[node name="scene" type="Node2D" index="0"]
script = ExtResource( 1 )
[node name="background" type="Sprite" parent="." index="0"]
[node name="dialog_layer" type="CanvasLayer" parent="." index="1"]
layer = 1
offset = Vector2( 0, 0 )
rotation = 0.0
scale = Vector2( 1, 1 )
transform = Transform2D( 1, 0, 0, 1, 0, 0 )
script = ExtResource( 2 )
dialog_path = "res://dialog/introduction.txt"
[node name="dialog_box" type="Control" parent="dialog_layer" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 1.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
script = ExtResource( 3 )
[node name="audio_player" type="AudioStreamPlayer" parent="dialog_layer/dialog_box" index="0"]
stream = null
volume_db = 0.0
pitch_scale = 1.0
autoplay = false
mix_target = 0
bus = "Master"
[node name="top_panel" type="HBoxContainer" parent="dialog_layer/dialog_box" index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 1.0
anchor_bottom = 0.0
margin_top = 16.0
margin_bottom = 20.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 1
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
alignment = 2
_sections_unfolded = [ "Margin", "Size Flags", "Theme", "custom_constants" ]
[node name="auto_btn" type="Button" parent="dialog_layer/dialog_box/top_panel" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 847.0
margin_right = 887.0
margin_bottom = 20.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color_disabled = Color( 0.378906, 0.378906, 0.378906, 1 )
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 1, 0.647059, 0, 1 )
custom_colors/font_color_pressed = Color( 1, 0.647059, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = "auto"
flat = true
align = 1
_sections_unfolded = [ "custom_constants" ]
[node name="ffwd_btn" type="Button" parent="dialog_layer/dialog_box/top_panel" index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 891.0
margin_right = 926.0
margin_bottom = 20.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color_disabled = Color( 0.378906, 0.378906, 0.378906, 1 )
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 1, 0.647059, 0, 1 )
custom_colors/font_color_pressed = Color( 1, 0.647059, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = "fast"
flat = true
align = 1
[node name="menu_btn" type="Button" parent="dialog_layer/dialog_box/top_panel" index="2"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 930.0
margin_right = 1002.0
margin_bottom = 20.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_colors/font_color_disabled = Color( 0.378906, 0.378906, 0.378906, 1 )
custom_colors/font_color = Color( 1, 1, 1, 1 )
custom_colors/font_color_hover = Color( 1, 0.647059, 0, 1 )
custom_colors/font_color_pressed = Color( 1, 0.647059, 0, 1 )
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
text = " | menu"
flat = true
align = 1
[node name="spacing" type="Control" parent="dialog_layer/dialog_box/top_panel" index="3"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 1006.0
margin_right = 1024.0
margin_bottom = 20.0
rect_min_size = Vector2( 18, 0 )
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
_sections_unfolded = [ "Rect" ]
[node name="body_container" type="PanelContainer" parent="dialog_layer/dialog_box" index="2"]
anchor_left = 0.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_top = -100.0
grow_vertical = 0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_styles/panel = SubResource( 1 )
_sections_unfolded = [ "Grow Direction", "Mouse", "Size Flags", "custom_styles" ]
[node name="bottom_panel" type="VBoxContainer" parent="dialog_layer/dialog_box/body_container" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 1024.0
margin_bottom = 100.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
alignment = 0
_sections_unfolded = [ "Grow Direction", "Mouse" ]
[node name="subject_margin" type="MarginContainer" parent="dialog_layer/dialog_box/body_container/bottom_panel" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 1024.0
margin_bottom = 14.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_constants/margin_right = 0
custom_constants/margin_top = 0
custom_constants/margin_left = 4
custom_constants/margin_bottom = 0
_sections_unfolded = [ "Mouse", "custom_constants" ]
[node name="subject_panel" type="HBoxContainer" parent="dialog_layer/dialog_box/body_container/bottom_panel/subject_margin" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 4.0
margin_right = 1024.0
margin_bottom = 14.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
alignment = 0
_sections_unfolded = [ "Mouse" ]
[node name="subject_label" type="Label" parent="dialog_layer/dialog_box/body_container/bottom_panel/subject_margin/subject_panel" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 60.0
margin_bottom = 14.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 4
text = "Arborator"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "Margin" ]
[node name="body_margin" type="MarginContainer" parent="dialog_layer/dialog_box/body_container/bottom_panel" index="1"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_top = 18.0
margin_right = 1024.0
margin_bottom = 48.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
custom_constants/margin_top = 8
custom_constants/margin_left = 8
custom_constants/margin_bottom = 8
_sections_unfolded = [ "Margin", "Mouse", "custom_constants" ]
[node name="body_label" type="Label" parent="dialog_layer/dialog_box/body_container/bottom_panel/body_margin" index="0"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_left = 8.0
margin_top = 8.0
margin_right = 1024.0
margin_bottom = 22.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 2
mouse_default_cursor_shape = 0
size_flags_horizontal = 3
size_flags_vertical = 4
text = "Sup new kid, you skippin?"
percent_visible = 1.0
lines_skipped = 0
max_lines_visible = -1
_sections_unfolded = [ "Margin", "Mouse", "Rect", "Size Flags" ]

38
scene_switcher.gd Normal file
View File

@ -0,0 +1,38 @@
extends Node
var current_scene
func _ready():
var root = get_tree().get_root()
current_scene = root.get_child(root.get_child_count() - 1)
func goto_scene(path, from_end = false):
call_deferred("_deferred_goto_scene", path, from_end)
func _deferred_goto_scene(path, from_end):
# Store old name so we can pass it to next one
var current_scene_name = current_scene.get_filename()
var old_scene = current_scene
current_scene.get_parent().remove_child(current_scene)
current_scene.queue_free()
# Load new scene
var s = ResourceLoader.load(path)
# Instance the new scene
current_scene = s.instance()
if "from_scene_name" in current_scene:
if current_scene_name != current_scene.get_filename():
current_scene.from_scene_name = current_scene_name
else:
if "from_scene_name" in old_scene:
current_scene.from_scene_name = old_scene.from_scene_name
# Add it to the active scene, as child of root
get_tree().get_root().add_child(current_scene)
# optional, to make it compatible with the SceneTree.change_scene() API
get_tree().set_current_scene(current_scene)

25
ui/dialog_choice.tscn Normal file
View File

@ -0,0 +1,25 @@
[gd_scene format=2]
[node name="dialog_choice" type="Button"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 40.0
margin_bottom = 40.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
focus_mode = 2
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1
toggle_mode = false
enabled_focus_mode = 2
shortcut = null
group = null
flat = false
align = 1

18
ui/dialog_choices.tscn Normal file
View File

@ -0,0 +1,18 @@
[gd_scene format=2]
[node name="dialog_choices" type="Control"]
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 40.0
margin_bottom = 40.0
rect_pivot_offset = Vector2( 0, 0 )
rect_clip_content = false
mouse_filter = 0
mouse_default_cursor_shape = 0
size_flags_horizontal = 1
size_flags_vertical = 1