summaryrefslogtreecommitdiff
path: root/addons/escoria-core/ui_library
diff options
context:
space:
mode:
authorRadio <radiohotline@disroot.org>2026-06-04 15:15:11 +0300
committerRadio <radiohotline@disroot.org>2026-06-04 15:15:11 +0300
commit9d74b49ab62908d4acf53dde444413886b8b28e5 (patch)
tree0cb785267ea7e239b19d88684c0d64b9bd5d52f0 /addons/escoria-core/ui_library
parent2c85d452ad02a5f89cd43bad7f9de31d0e4fa0c1 (diff)
escoria setup and old branch archives
Diffstat (limited to 'addons/escoria-core/ui_library')
-rw-r--r--addons/escoria-core/ui_library/fonts/caslonantique.tres7
-rw-r--r--addons/escoria-core/ui_library/fonts/caslonantique.ttfbin0 -> 147756 bytes
-rw-r--r--addons/escoria-core/ui_library/fonts/caslonantique.ttf.import36
-rw-r--r--addons/escoria-core/ui_library/inventory/esc_inventory_button.gd178
-rw-r--r--addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/inventory/esc_inventory_container.gd102
-rw-r--r--addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd11
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/load/load_game.gd58
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn44
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd7
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn35
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd14
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn63
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/save_game.gd90
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn56
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd21
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn71
-rw-r--r--addons/escoria-core/ui_library/menus/main_menu/main_menu.gd42
-rw-r--r--addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn85
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/de.pngbin0 -> 2245 bytes
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/de.png.import40
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/en.pngbin0 -> 8760 bytes
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/en.png.import40
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/es.pngbin0 -> 5210 bytes
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/es.png.import40
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/fr.pngbin0 -> 1840 bytes
-rw-r--r--addons/escoria-core/ui_library/menus/options/flags/fr.png.import40
-rw-r--r--addons/escoria-core/ui_library/menus/options/options.gd190
-rw-r--r--addons/escoria-core/ui_library/menus/options/options.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/options/options.tscn136
-rw-r--r--addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd75
-rw-r--r--addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn94
-rw-r--r--addons/escoria-core/ui_library/theme.tres6
-rw-r--r--addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd14
-rw-r--r--addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn23
-rw-r--r--addons/escoria-core/ui_library/tools/room_select/room_select.gd73
-rw-r--r--addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid1
-rw-r--r--addons/escoria-core/ui_library/tools/room_select/room_select.tscn25
48 files changed, 1729 insertions, 0 deletions
diff --git a/addons/escoria-core/ui_library/fonts/caslonantique.tres b/addons/escoria-core/ui_library/fonts/caslonantique.tres
new file mode 100644
index 0000000..19bcb83
--- /dev/null
+++ b/addons/escoria-core/ui_library/fonts/caslonantique.tres
@@ -0,0 +1,7 @@
+[gd_resource type="FontFile" load_steps=2 format=2]
+
+[ext_resource path="res://addons/escoria-core/ui_library/fonts/caslonantique.ttf" type="FontFile" id=1]
+
+[resource]
+size = 21
+font_data = ExtResource( 1 )
diff --git a/addons/escoria-core/ui_library/fonts/caslonantique.ttf b/addons/escoria-core/ui_library/fonts/caslonantique.ttf
new file mode 100644
index 0000000..eec6f63
--- /dev/null
+++ b/addons/escoria-core/ui_library/fonts/caslonantique.ttf
Binary files differ
diff --git a/addons/escoria-core/ui_library/fonts/caslonantique.ttf.import b/addons/escoria-core/ui_library/fonts/caslonantique.ttf.import
new file mode 100644
index 0000000..b23d333
--- /dev/null
+++ b/addons/escoria-core/ui_library/fonts/caslonantique.ttf.import
@@ -0,0 +1,36 @@
+[remap]
+
+importer="font_data_dynamic"
+type="FontFile"
+uid="uid://dmfspabef0y2f"
+path="res://.godot/imported/caslonantique.ttf-d88d3fe7dc88ba83bb382c882d7923fe.fontdata"
+
+[deps]
+
+source_file="res://addons/escoria-core/ui_library/fonts/caslonantique.ttf"
+dest_files=["res://.godot/imported/caslonantique.ttf-d88d3fe7dc88ba83bb382c882d7923fe.fontdata"]
+
+[params]
+
+Rendering=null
+antialiasing=1
+generate_mipmaps=false
+disable_embedded_bitmaps=true
+multichannel_signed_distance_field=false
+msdf_pixel_range=8
+msdf_size=48
+allow_system_fallback=true
+force_autohinter=false
+modulate_color_glyphs=false
+hinting=1
+subpixel_positioning=4
+keep_rounding_remainders=true
+oversampling=0.0
+Fallbacks=null
+fallbacks=[]
+Compress=null
+compress=true
+preload=[]
+language_support={}
+script_support={}
+opentype_features={}
diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd
new file mode 100644
index 0000000..b109c38
--- /dev/null
+++ b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd
@@ -0,0 +1,178 @@
+## The inventory representation of an ESC item if pickable (only used by
+## the inventory components)
+extends TextureButton
+class_name ESCInventoryButton
+
+## Signal emitted when the item was left clicked.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |item_id|`Variant`|Global ID of the clicked item|yes|[br]
+## [br]
+signal mouse_left_inventory_item(item_id)
+
+## Signal emitted when the item was right clicked.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |item_id|`Variant`|Global ID of the clicked item|yes|[br]
+## [br]
+signal mouse_right_inventory_item(item_id)
+
+## Signal emitted when the item was double clicked.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |item_id|`Variant`|Global ID of the clicked item|yes|[br]
+## [br]
+signal mouse_double_left_inventory_item(item_id)
+
+## Signal emitted when the item was focused.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |item_id|`Variant`|Global ID of the clicked item|yes|[br]
+## [br]
+signal inventory_item_focused(item_id)
+
+## Signal emitted when the item is not focused anymore.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+signal inventory_item_unfocused()
+
+## Global ID of the ESCItem that uses this ESCInventoryItem.
+var global_id: String = ""
+
+
+
+## Initializes the inventory button with the given item.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |p_item|`ESCInventoryItem`|The ESCInventoryItem to represent.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _init(p_item: ESCInventoryItem) -> void:
+ global_id = p_item.global_id
+ texture_normal = p_item.texture_normal
+ texture_hover = p_item.texture_hovered
+ stretch_mode = TextureButton.STRETCH_KEEP_ASPECT
+
+
+
+## Updates the size of the inventory button every frame.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |_delta|`float`|The frame time delta (unused).|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _process(_delta: float) -> void:
+ size = ProjectSettings.get_setting("escoria/ui/inventory_item_size")
+ custom_minimum_size = ProjectSettings.get_setting(
+ "escoria/ui/inventory_item_size"
+ )
+
+
+## Connects input handlers for the inventory button.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _ready():
+ gui_input.connect(_on_inventory_item_gui_input)
+ mouse_entered.connect(_on_inventory_item_mouse_enter)
+ mouse_exited.connect(_on_inventory_item_mouse_exit)
+
+
+
+## Handles the gui input and emits the respective signals.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |event|`InputEvent`|The event received.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _on_inventory_item_gui_input(event: InputEvent):
+ if event is InputEventMouseButton and event.is_pressed():
+ if event.button_index == MOUSE_BUTTON_WHEEL_UP:
+ escoria.inputs_manager._on_mousewheel_action(-1)
+ get_viewport().set_input_as_handled()
+ elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
+ escoria.inputs_manager._on_mousewheel_action(1)
+ get_viewport().set_input_as_handled()
+ elif event.double_click:
+ if event.button_index == MOUSE_BUTTON_LEFT:
+ mouse_double_left_inventory_item.emit(
+ global_id,
+ event
+ )
+ # Make sure fast right clicks in the inventory aren't ignored
+ elif event.button_index == MOUSE_BUTTON_RIGHT:
+ mouse_right_inventory_item.emit(
+ global_id,
+ event
+ )
+ else:
+ if event.button_index == MOUSE_BUTTON_LEFT:
+ mouse_left_inventory_item.emit(
+ global_id,
+ event
+ )
+ if event.button_index == MOUSE_BUTTON_RIGHT:
+ mouse_right_inventory_item.emit(
+ global_id,
+ event
+ )
+
+
+## Handles mouse entering the item and sends the respective signal.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _on_inventory_item_mouse_enter():
+ inventory_item_focused.emit(global_id)
+
+## Handles mouse leaving the item and sends the respective signal.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _on_inventory_item_mouse_exit():
+ inventory_item_unfocused.emit()
diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid
new file mode 100644
index 0000000..3fdb883
--- /dev/null
+++ b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid
@@ -0,0 +1 @@
+uid://c1hfi1dlwh3hq
diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd
new file mode 100644
index 0000000..1e6dab8
--- /dev/null
+++ b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd
@@ -0,0 +1,102 @@
+
+## Inventory container handler that acts as a base for UIs inventory containers.
+extends Control
+class_name ESCInventoryContainer
+
+## Whether any item in the container is currently focused.[br]
+var item_focused: bool = false
+
+## Whether the inventory container currently is empty.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns whether the inventory container currently is empty. Whether the container is empty or not. (`bool`)
+func is_empty() -> bool:
+ return get_child_count() > 0
+
+## Adds a new item into the container and returns the control generated for it so its events can be handled by the inputs manager.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |inventory_item|`ESCInventoryItem`|Item to add.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns the button generated for the item. (`ESCInventoryButton`)
+func add_item(inventory_item: ESCInventoryItem) -> ESCInventoryButton:
+ var button = ESCInventoryButton.new(inventory_item)
+ button.inventory_item_focused.connect(_on_inventory_item_focused)
+ button.inventory_item_unfocused.connect(_on_inventory_item_unfocused)
+ add_child(button)
+ return button
+
+## Removes an item from the container.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |inventory_item|`ESCInventoryItem`|Item to remove.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func remove_item(inventory_item: ESCInventoryItem):
+ for c in get_children():
+ if c is ESCInventoryButton and c.global_id == inventory_item.global_id:
+ remove_child(c)
+ c.queue_free()
+
+
+## An Inventory button from the container, using an ESCInventoryItem.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |inventory_item|`ESCInventoryItem`|Inventory item to return the button node from.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns an Inventory button from the container, using an ESCInventoryItem. The inventory button node, or null if not found. (`ESCInventoryButton`)
+func get_inventory_button(inventory_item: ESCInventoryItem) -> ESCInventoryButton:
+ var inventory_button = null
+ for c in get_children():
+ if c.global_id == inventory_item.global_id:
+ inventory_button = c
+ break
+ return inventory_button
+
+
+## Called when an inventory item is focused.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |item_id|`Variant`|The global ID of the focused item.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _on_inventory_item_focused(item_id):
+ item_focused = true
+
+
+## Called when an inventory item is unfocused.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func _on_inventory_item_unfocused():
+ item_focused = false
diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid
new file mode 100644
index 0000000..1ad5d75
--- /dev/null
+++ b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid
@@ -0,0 +1 @@
+uid://c4syt26p7mg66
diff --git a/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd
new file mode 100644
index 0000000..975076f
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd
@@ -0,0 +1,11 @@
+class_name SaveGamesSorter
+
+static func sort_by_date_descending(a, b):
+ if Time.get_unix_time_from_datetime_dict(a.date) > Time.get_unix_time_from_datetime_dict(b.date):
+ return true
+ return false
+
+static func sort_by_date_ascending(a, b):
+ if Time.get_unix_time_from_datetime_dict(a.date) < Time.get_unix_time_from_datetime_dict(b.date):
+ return true
+ return false
diff --git a/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid
new file mode 100644
index 0000000..429faf6
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid
@@ -0,0 +1 @@
+uid://cx3y8rl2vtp8a
diff --git a/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd
new file mode 100644
index 0000000..c0919fc
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd
@@ -0,0 +1,58 @@
+# A container for loading from a save slot
+extends Control
+
+
+# Emitted when the back button is pressed
+signal back_button_pressed
+
+
+# The scene to display a slot
+@export var slot_ui_scene: PackedScene
+
+
+# Load the savegames when loaded
+func _ready():
+ refresh_savegames()
+
+
+# A slot was pressed. Load the game
+#
+# #### Parameters
+# - slot_id: The slot that was pressed
+func _on_slot_pressed(slot_id: int) -> void:
+ escoria.save_manager.load_game(slot_id)
+
+
+# The back button was pressed
+func _on_back_pressed():
+ back_button_pressed.emit()
+
+
+# Create the slots from the list of savegames
+func refresh_savegames():
+ # Clear all UI saves slots
+ for slot in $VBoxContainer/ScrollContainer/slots.get_children():
+ $VBoxContainer/ScrollContainer/slots.remove_child(slot)
+
+ # Get saves list
+ var saves_list = escoria.save_manager.get_saves_list()
+ if saves_list.values().is_empty():
+ return
+ var saves_array: Array = saves_list.values()
+ saves_array.sort_custom(Callable(SaveGamesSorter, "sort_by_date_descending"))
+
+ for save in saves_array:
+ var new_slot = slot_ui_scene.instantiate()
+ $VBoxContainer/ScrollContainer/slots.add_child(
+ new_slot
+ )
+
+ var datetime_string = "%02d/%02d/%02d %02d:%02d" % [
+ save.date["day"],
+ save.date["month"],
+ save.date["year"],
+ save.date["hour"],
+ save.date["minute"],
+ ]
+ new_slot.set_slot_name_date(save["name"], datetime_string)
+ new_slot.connect("pressed", Callable(self, "_on_slot_pressed").bind(int(save["slotnumber"])))
diff --git a/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid
new file mode 100644
index 0000000..1a2ed80
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid
@@ -0,0 +1 @@
+uid://yfabboc406rf
diff --git a/addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn b/addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn
new file mode 100644
index 0000000..58fbcf2
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn
@@ -0,0 +1,44 @@
+[gd_scene load_steps=3 format=3 uid="uid://j3wkbyhedkpr"]
+
+[ext_resource type="PackedScene" uid="uid://ciysuxd81qucn" path="res://addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn" id="2"]
+[ext_resource type="Script" uid="uid://yfabboc406rf" path="res://addons/escoria-core/ui_library/menus/load_save/load/load_game.gd" id="3"]
+
+[node name="load_game" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("3")
+slot_ui_scene = ExtResource("2")
+
+[node name="Panel" type="ColorRect" parent="."]
+layout_mode = 0
+anchor_right = 1.0
+anchor_bottom = 1.0
+color = Color(0.270588, 0.270588, 0.270588, 1)
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 0
+anchor_left = 0.3
+anchor_top = 0.3
+anchor_right = 0.7
+anchor_bottom = 0.7
+offset_left = -55.5
+offset_right = 55.5
+
+[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="slots" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="back" type="Button" parent="VBoxContainer"]
+layout_mode = 2
+text = "OPTIONS_BACK"
+
+[connection signal="pressed" from="VBoxContainer/back" to="." method="_on_back_pressed"]
diff --git a/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd
new file mode 100644
index 0000000..9de5d90
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd
@@ -0,0 +1,7 @@
+extends Button
+
+
+func set_slot_name_date(p_name: String, p_date: String):
+ $VBoxContainer/slot_name.text = p_name
+ $VBoxContainer/date.text = p_date
+
diff --git a/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid
new file mode 100644
index 0000000..f48bf6e
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid
@@ -0,0 +1 @@
+uid://de0wab1dx7xfx
diff --git a/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn
new file mode 100644
index 0000000..79f3a74
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn
@@ -0,0 +1,35 @@
+[gd_scene load_steps=2 format=3 uid="uid://ciysuxd81qucn"]
+
+[ext_resource type="Script" uid="uid://de0wab1dx7xfx" path="res://addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd" id="2"]
+
+[node name="slot" type="Button"]
+offset_right = 665.0
+offset_bottom = 86.0
+custom_minimum_size = Vector2(0, 100)
+size_flags_horizontal = 3
+script = ExtResource("2")
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+alignment = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="slot_name" type="Label" parent="VBoxContainer"]
+offset_top = 34.0
+offset_right = 665.0
+offset_bottom = 48.0
+text = "slot_name"
+align = 1
+
+[node name="date" type="Label" parent="VBoxContainer"]
+offset_top = 52.0
+offset_right = 665.0
+offset_bottom = 66.0
+text = "date"
+align = 1
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd
new file mode 100644
index 0000000..6d7e88f
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd
@@ -0,0 +1,14 @@
+extends Popup
+
+signal confirm_yes
+
+func _ready():
+ $MarginContainer/VBoxContainer/HBoxContainer/yes.grab_focus()
+
+func _on_no_pressed():
+ hide()
+
+
+func _on_yes_pressed():
+ confirm_yes.emit()
+ hide()
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid
new file mode 100644
index 0000000..5662ea7
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid
@@ -0,0 +1 @@
+uid://m1jk0vg7pdn2
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn
new file mode 100644
index 0000000..e345f01
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn
@@ -0,0 +1,63 @@
+[gd_scene load_steps=2 format=3 uid="uid://c4ldxsprk70gi"]
+
+[ext_resource type="Script" uid="uid://m1jk0vg7pdn2" path="res://addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd" id="2"]
+
+[node name="overwrite_confirm_popup" type="Popup"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+script = ExtResource("2")
+
+[node name="MarginContainer" type="MarginContainer" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+theme_override_constants/margin_right = 30
+theme_override_constants/margin_top = 30
+theme_override_constants/margin_left = 30
+theme_override_constants/margin_bottom = 30
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
+offset_left = 30.0
+offset_top = 423.0
+offset_right = 1250.0
+offset_bottom = 477.0
+size_flags_vertical = 4
+theme_override_constants/separation = 20
+
+[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
+offset_right = 1220.0
+offset_bottom = 14.0
+text = "CONFIRM_OVERWRITE"
+
+[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
+offset_top = 34.0
+offset_right = 1220.0
+offset_bottom = 54.0
+theme_override_constants/separation = 10
+alignment = 2
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="no" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
+offset_left = 1187.0
+offset_right = 1220.0
+offset_bottom = 20.0
+text = "NO"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="yes" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
+offset_left = 1144.0
+offset_right = 1177.0
+offset_bottom = 20.0
+text = "YES"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/no" to="." method="_on_no_pressed"]
+[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/yes" to="." method="_on_yes_pressed"]
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd
new file mode 100644
index 0000000..33bbb13
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd
@@ -0,0 +1,90 @@
+# A container for saving to a save slot
+extends Control
+
+# Emitted when the back button is pressed
+signal back_button_pressed
+
+
+# The scene to display a slot
+@export var slot_ui_scene: PackedScene
+
+
+# The slot that was pressed
+var _slot_pressed = null
+
+
+# Load the savegames when loaded
+func _ready():
+ refresh_savegames()
+
+
+# A slot was pressed, save the game
+func _on_slot_pressed(p_slot_n: int):
+ _slot_pressed = p_slot_n
+ if escoria.save_manager.save_game_exists(p_slot_n):
+ $overwrite_confirm_popup.popup_centered_ratio(.3)
+ else:
+ $save_name_popup.popup_centered_ratio(.3)
+
+
+# Create the slots from the list of savegames
+func refresh_savegames():
+ var _slots = $VBoxContainer/ScrollContainer/slots
+ for slot in _slots.get_children():
+ _slots.remove_child(slot)
+
+ var saves_list = escoria.save_manager.get_saves_list()
+
+ var datetime = Time.get_datetime_dict_from_system()
+ var datetime_string = "%02d/%02d/%02d %02d:%02d" % [
+ datetime["day"],
+ datetime["month"],
+ datetime["year"],
+ datetime["hour"],
+ datetime["minute"],
+ ]
+ var new_slot = slot_ui_scene.instantiate()
+ _slots.add_child(new_slot)
+ _slots.move_child(new_slot, 0)
+ new_slot.set_slot_name_date(tr("New save"), datetime_string)
+ new_slot.connect("pressed", Callable(self, "_on_slot_pressed").bind(saves_list.size()+1))
+
+ if saves_list.values().is_empty():
+ return
+ var saves_array: Array = saves_list.values()
+ saves_array.sort_custom(Callable(SaveGamesSorter, "sort_by_date_descending"))
+
+ for save in saves_array:
+ new_slot = slot_ui_scene.instantiate()
+ _slots.add_child(new_slot)
+
+ datetime_string = "%02d/%02d/%02d %02d:%02d" % [
+ save.date["day"],
+ save.date["month"],
+ save.date["year"],
+ save.date["hour"],
+ save.date["minute"],
+ ]
+
+ new_slot.set_slot_name_date(save["name"], datetime_string)
+ new_slot.connect("pressed", Callable(self, "_on_slot_pressed").bind(int(save["slotnumber"])))
+
+
+# The back button was pressed
+func _on_back_pressed():
+ back_button_pressed.emit()
+
+
+# The name for the save game was given, save the game.
+#
+# #### Parameters
+# - p_savename: The name of the savegame entered
+func _on_save_name_popup_savegame_name_ok(p_savename: String):
+ escoria.save_manager.save_game(_slot_pressed, p_savename)
+ refresh_savegames()
+ _slot_pressed = null
+
+
+# Overwriting the savegame was confirmed, show the save name popup
+func _on_overwrite_confirm_popup_confirm_yes():
+ $save_name_popup.popup_centered_ratio(.3)
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid
new file mode 100644
index 0000000..359837e
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid
@@ -0,0 +1 @@
+uid://crpi3w8bdekpc
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn b/addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn
new file mode 100644
index 0000000..e9df2b4
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn
@@ -0,0 +1,56 @@
+[gd_scene load_steps=5 format=3 uid="uid://cj440t8pub603"]
+
+[ext_resource type="PackedScene" uid="uid://ciysuxd81qucn" path="res://addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn" id="1"]
+[ext_resource type="Script" uid="uid://crpi3w8bdekpc" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_game.gd" id="2"]
+[ext_resource type="PackedScene" uid="uid://coiqm0xcq8f8o" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn" id="4"]
+[ext_resource type="PackedScene" uid="uid://c4ldxsprk70gi" path="res://addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn" id="5"]
+
+[node name="save_game" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("2")
+slot_ui_scene = ExtResource("1")
+
+[node name="Panel" type="ColorRect" parent="."]
+layout_mode = 0
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_top = -1.0
+offset_bottom = -1.0
+color = Color(0.270588, 0.270588, 0.270588, 1)
+
+[node name="save_name_popup" parent="." instance=ExtResource("4")]
+size = Vector2i(209, 185)
+
+[node name="overwrite_confirm_popup" parent="." instance=ExtResource("5")]
+size = Vector2i(231, 134)
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 0
+anchor_left = 0.3
+anchor_top = 0.3
+anchor_right = 0.7
+anchor_bottom = 0.7
+offset_left = -55.5
+offset_right = 55.5
+
+[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="slots" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="back" type="Button" parent="VBoxContainer"]
+layout_mode = 2
+text = "OPTIONS_BACK"
+
+[connection signal="savegame_name_ok" from="save_name_popup" to="." method="_on_save_name_popup_savegame_name_ok"]
+[connection signal="confirm_yes" from="overwrite_confirm_popup" to="." method="_on_overwrite_confirm_popup_confirm_yes"]
+[connection signal="pressed" from="VBoxContainer/back" to="." method="_on_back_pressed"]
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd
new file mode 100644
index 0000000..b631ef2
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd
@@ -0,0 +1,21 @@
+extends Popup
+
+signal savegame_name_ok(savegame_name)
+signal savegame_cancel
+
+func _on_cancel_pressed():
+ savegame_cancel.emit()
+ hide()
+
+func _on_ok_pressed():
+ if not $MarginContainer/VBoxContainer/LineEdit.text.is_empty():
+ savegame_name_ok.emit($MarginContainer/VBoxContainer/LineEdit.text)
+ $MarginContainer/VBoxContainer/LineEdit.clear()
+ hide()
+
+func _input(event):
+ if event is InputEventKey:
+ if event.is_pressed() \
+ and event.keycode == KEY_ENTER \
+ or event.keycode == KEY_KP_ENTER:
+ _on_ok_pressed()
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid
new file mode 100644
index 0000000..41128a4
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid
@@ -0,0 +1 @@
+uid://blqbjhwtenth5
diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn
new file mode 100644
index 0000000..8d6fc57
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn
@@ -0,0 +1,71 @@
+[gd_scene load_steps=2 format=3 uid="uid://coiqm0xcq8f8o"]
+
+[ext_resource type="Script" uid="uid://blqbjhwtenth5" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd" id="2"]
+
+[node name="save_name_popup" type="Popup"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+script = ExtResource("2")
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="MarginContainer" type="MarginContainer" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+theme_override_constants/margin_right = 30
+theme_override_constants/margin_top = 30
+theme_override_constants/margin_left = 30
+theme_override_constants/margin_bottom = 30
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
+offset_left = 30.0
+offset_top = 401.0
+offset_right = 1250.0
+offset_bottom = 499.0
+size_flags_vertical = 4
+theme_override_constants/separation = 20
+
+[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
+offset_right = 1220.0
+offset_bottom = 14.0
+text = "ENTER_SAVE_NAME"
+
+[node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"]
+offset_top = 34.0
+offset_right = 1220.0
+offset_bottom = 58.0
+
+[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
+offset_top = 78.0
+offset_right = 1220.0
+offset_bottom = 98.0
+theme_override_constants/separation = 10
+alignment = 2
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="cancel" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
+offset_left = 1118.0
+offset_right = 1179.0
+offset_bottom = 20.0
+text = "CANCEL"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="ok" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
+offset_left = 1189.0
+offset_right = 1220.0
+offset_bottom = 20.0
+text = "OK"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/cancel" to="." method="_on_cancel_pressed"]
+[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/ok" to="." method="_on_ok_pressed"]
diff --git a/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd
new file mode 100644
index 0000000..dac988b
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd
@@ -0,0 +1,42 @@
+# A simple main menu
+extends Control
+
+
+# Start the game
+func _on_new_game_pressed():
+ escoria.new_game()
+
+
+# Show the load slots
+func _on_load_game_pressed():
+ $main.hide()
+ $load_game.refresh_savegames()
+ $load_game.show()
+
+
+# Show the options panel
+func _on_options_pressed():
+ $main.hide()
+ $options.show()
+
+
+# Quit the game
+func _on_quit_pressed():
+ escoria.quit()
+
+
+# Hide the options panel again
+func _on_options_back_button_pressed():
+ reset()
+
+
+# Hide the load panel
+func _on_load_game_back_button_pressed():
+ reset()
+
+
+# Resets the UI to initial state
+func reset():
+ $load_game.hide()
+ $options.hide()
+ $main.show()
diff --git a/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid
new file mode 100644
index 0000000..467bea5
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid
@@ -0,0 +1 @@
+uid://cfpa1dldiuvh8
diff --git a/addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn b/addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn
new file mode 100644
index 0000000..b259fa1
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn
@@ -0,0 +1,85 @@
+[gd_scene load_steps=6 format=3 uid="uid://u8an5av5fuxx"]
+
+[ext_resource type="Script" uid="uid://cfpa1dldiuvh8" path="res://addons/escoria-core/ui_library/menus/main_menu/main_menu.gd" id="1"]
+[ext_resource type="Theme" uid="uid://b081yyinnj03t" path="res://addons/escoria-core/ui_library/theme.tres" id="2"]
+[ext_resource type="Texture2D" uid="uid://bwplh73qf2twk" path="res://addons/escoria-core/design/escoria-logo-small.png" id="3"]
+[ext_resource type="PackedScene" uid="uid://bhcfnds3yy2yv" path="res://addons/escoria-core/ui_library/menus/options/options.tscn" id="4"]
+[ext_resource type="PackedScene" uid="uid://j3wkbyhedkpr" path="res://addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn" id="5"]
+
+[node name="main_menu" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme = ExtResource("2")
+script = ExtResource("1")
+
+[node name="load_game" parent="." instance=ExtResource("5")]
+visible = false
+layout_mode = 1
+
+[node name="options" parent="." instance=ExtResource("4")]
+visible = false
+layout_mode = 1
+
+[node name="main" type="Control" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="ColorRect" type="ColorRect" parent="main"]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+color = Color(0.270222, 0.270222, 0.270222, 1)
+
+[node name="main" type="VBoxContainer" parent="main"]
+layout_mode = 0
+anchor_left = 0.5
+anchor_right = 0.5
+anchor_bottom = 1.0
+offset_left = -308.0
+offset_right = 308.0
+theme_override_constants/separation = 100
+alignment = 1
+
+[node name="TextureRect" type="TextureRect" parent="main/main"]
+layout_mode = 2
+texture = ExtResource("3")
+
+[node name="buttons" type="VBoxContainer" parent="main/main"]
+layout_mode = 2
+theme_override_constants/separation = 10
+
+[node name="new_game" type="Button" parent="main/main/buttons"]
+custom_minimum_size = Vector2(0, 150)
+layout_mode = 2
+size_flags_vertical = 3
+text = "NEW_GAME"
+
+[node name="load_game" type="Button" parent="main/main/buttons"]
+layout_mode = 2
+text = "LOAD_GAME"
+
+[node name="options" type="Button" parent="main/main/buttons"]
+layout_mode = 2
+text = "OPTIONS"
+
+[node name="quit" type="Button" parent="main/main/buttons"]
+layout_mode = 2
+text = "QUIT"
+
+[connection signal="back_button_pressed" from="load_game" to="." method="_on_load_game_back_button_pressed"]
+[connection signal="back_button_pressed" from="options" to="." method="_on_options_back_button_pressed"]
+[connection signal="pressed" from="main/main/buttons/new_game" to="." method="_on_new_game_pressed"]
+[connection signal="pressed" from="main/main/buttons/load_game" to="." method="_on_load_game_pressed"]
+[connection signal="pressed" from="main/main/buttons/options" to="." method="_on_options_pressed"]
+[connection signal="pressed" from="main/main/buttons/quit" to="." method="_on_quit_pressed"]
diff --git a/addons/escoria-core/ui_library/menus/options/flags/de.png b/addons/escoria-core/ui_library/menus/options/flags/de.png
new file mode 100644
index 0000000..4fe98a8
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/de.png
Binary files differ
diff --git a/addons/escoria-core/ui_library/menus/options/flags/de.png.import b/addons/escoria-core/ui_library/menus/options/flags/de.png.import
new file mode 100644
index 0000000..f4b4dc8
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/de.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://r6b3dp6ipv5k"
+path="res://.godot/imported/de.png-eb10e96b44c694832a61fe6e6648faac.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/escoria-core/ui_library/menus/options/flags/de.png"
+dest_files=["res://.godot/imported/de.png-eb10e96b44c694832a61fe6e6648faac.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/escoria-core/ui_library/menus/options/flags/en.png b/addons/escoria-core/ui_library/menus/options/flags/en.png
new file mode 100644
index 0000000..0589284
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/en.png
Binary files differ
diff --git a/addons/escoria-core/ui_library/menus/options/flags/en.png.import b/addons/escoria-core/ui_library/menus/options/flags/en.png.import
new file mode 100644
index 0000000..309ad2b
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/en.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cxuxfcyqd81c2"
+path="res://.godot/imported/en.png-e3d7ed70a1e59fdf7b47a2d8c96ff41f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/escoria-core/ui_library/menus/options/flags/en.png"
+dest_files=["res://.godot/imported/en.png-e3d7ed70a1e59fdf7b47a2d8c96ff41f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/escoria-core/ui_library/menus/options/flags/es.png b/addons/escoria-core/ui_library/menus/options/flags/es.png
new file mode 100644
index 0000000..7178ce4
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/es.png
Binary files differ
diff --git a/addons/escoria-core/ui_library/menus/options/flags/es.png.import b/addons/escoria-core/ui_library/menus/options/flags/es.png.import
new file mode 100644
index 0000000..378230c
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/es.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bdw1c55s4iaf4"
+path="res://.godot/imported/es.png-90c18482f305496440586f74957db9fb.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/escoria-core/ui_library/menus/options/flags/es.png"
+dest_files=["res://.godot/imported/es.png-90c18482f305496440586f74957db9fb.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/escoria-core/ui_library/menus/options/flags/fr.png b/addons/escoria-core/ui_library/menus/options/flags/fr.png
new file mode 100644
index 0000000..fcaf2cc
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/fr.png
Binary files differ
diff --git a/addons/escoria-core/ui_library/menus/options/flags/fr.png.import b/addons/escoria-core/ui_library/menus/options/flags/fr.png.import
new file mode 100644
index 0000000..a279aa7
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/flags/fr.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dfixfxtbgelrb"
+path="res://.godot/imported/fr.png-6fb37573278571598430825ea325f0e3.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/escoria-core/ui_library/menus/options/flags/fr.png"
+dest_files=["res://.godot/imported/fr.png-6fb37573278571598430825ea325f0e3.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/addons/escoria-core/ui_library/menus/options/options.gd b/addons/escoria-core/ui_library/menus/options/options.gd
new file mode 100644
index 0000000..32a9758
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/options.gd
@@ -0,0 +1,190 @@
+# An options menu
+extends Control
+
+
+# The back button was pressed
+signal back_button_pressed
+
+
+# Custom setting key
+const CUSTOM_SETTING: String = "a_custom_setting"
+
+
+# The current settings
+var backup_settings
+
+
+# A list of languages already added to the language selection
+var _loaded_languages: Array = []
+
+
+# The settings changed
+@onready var changed = false
+
+
+# Initialize the flags
+func _ready() -> void:
+ var _flags_container: HBoxContainer = \
+ $VBoxContainer/MarginContainer/options/flags
+ for child in _flags_container.get_children():
+ _flags_container.remove_child(child)
+ child.queue_free()
+
+ _loaded_languages = []
+
+ for lang in TranslationServer.get_loaded_locales():
+ if not lang in _loaded_languages:
+ _loaded_languages.append(lang)
+ var _lang = TextureRect.new()
+ _lang.texture = load(
+ "res://addons/escoria-core/ui_library" + \
+ "/menus/options/flags/%s.png" % lang
+ )
+ _flags_container.add_child(_lang)
+ _lang.connect("gui_input", Callable(self, "_on_language_input").bind(lang))
+
+
+# Show the options
+func show():
+ backup_settings = escoria.settings_manager.get_settings()
+ initialize_options(backup_settings)
+ visible = true
+
+
+# Set the sliders to the values of the settings
+#
+# #### Parameters
+# - p_settings: The settings to use
+func initialize_options(p_settings):
+ var _options = $VBoxContainer/MarginContainer/options
+ _options.get_node("general_volume").value = p_settings["master_volume"]
+ _options.get_node("sound_volume").value = p_settings["sfx_volume"]
+ _options.get_node("music_volume").value = p_settings["music_volume"]
+ _options.get_node("speech_volume").value = p_settings["speech_volume"]
+ _options.get_node("ambient_volume").value = p_settings["ambient_volume"]
+ _options.get_node("fullscreen").set_pressed_no_signal(p_settings["fullscreen"])
+
+
+# The language was changed
+#
+# #### Parameters
+# - event: The input event from the flag
+# - language: The language to set
+func _on_language_input(event: InputEvent, language: String):
+ if event.is_pressed():
+ TranslationServer.set_locale(language)
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.TEXT_LANG,
+ language
+ )
+ changed = true
+
+
+# Sound volume was changed
+#
+# #### Parameters
+# - value: The new volume level
+func _on_sound_volume_changed(value):
+ if ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.SFX_VOLUME
+ ) != value:
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.SFX_VOLUME,
+ value
+ )
+ escoria.settings_manager.apply_settings()
+ changed = true
+
+
+# Music volume was changed
+#
+# #### Parameters
+# - value: The new volume level
+func _on_music_volume_changed(value):
+ if ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.MUSIC_VOLUME
+ ) != value:
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.MUSIC_VOLUME,
+ value
+ )
+ escoria.settings_manager.apply_settings()
+ changed = true
+
+
+# General volume was changed
+#
+# #### Parameters
+# - value: The new volume level
+func _on_general_volume_changed(value):
+ if ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.MASTER_VOLUME
+ ) != value:
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.MASTER_VOLUME,
+ value
+ )
+ escoria.settings_manager.apply_settings()
+ changed = true
+
+
+# Speech volume was changed
+#
+# #### Parameters
+# - value: The new volume level
+func _on_speech_volume_value_changed(value: float) -> void:
+ if ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.SPEECH_VOLUME
+ ) != value:
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.SPEECH_VOLUME,
+ value
+ )
+ escoria.settings_manager.apply_settings()
+ changed = true
+
+# Ambient volume was changed
+#
+# #### Parameters
+# - value: The new volume level
+func _on_ambient_volume_value_changed(value: float) -> void:
+ if ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.AMBIENT_VOLUME
+ ) != value:
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.AMBIENT_VOLUME,
+ value
+ )
+ escoria.settings_manager.apply_settings()
+ changed = true
+
+
+# Fullscreen was changed
+#
+# #### Parameters
+# - button_pressed: Fullscreen (true) or windowed (false)
+func _on_fullscreen_toggled(button_pressed: bool) -> void:
+ if ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.FULLSCREEN
+ ) != button_pressed:
+ ESCProjectSettingsManager.set_setting(
+ ESCProjectSettingsManager.FULLSCREEN,
+ button_pressed
+ )
+ escoria.settings_manager.apply_settings()
+ changed = true
+
+
+# Save the settings
+func _on_apply_pressed():
+ escoria.settings_manager.custom_settings[CUSTOM_SETTING] = 100
+ escoria.settings_manager.save_settings()
+ changed = false
+ back_button_pressed.emit()
+
+
+# The back button was pressed
+func _on_back_pressed():
+ escoria.settings_manager.save_settings_resource_to_project_settings(backup_settings)
+ escoria.settings_manager.apply_settings()
+ back_button_pressed.emit()
diff --git a/addons/escoria-core/ui_library/menus/options/options.gd.uid b/addons/escoria-core/ui_library/menus/options/options.gd.uid
new file mode 100644
index 0000000..b7bb93d
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/options.gd.uid
@@ -0,0 +1 @@
+uid://btyrwps5h2qn7
diff --git a/addons/escoria-core/ui_library/menus/options/options.tscn b/addons/escoria-core/ui_library/menus/options/options.tscn
new file mode 100644
index 0000000..133315c
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/options/options.tscn
@@ -0,0 +1,136 @@
+[gd_scene load_steps=3 format=3 uid="uid://bhcfnds3yy2yv"]
+
+[ext_resource type="Texture2D" uid="uid://r6b3dp6ipv5k" path="res://addons/escoria-core/ui_library/menus/options/flags/de.png" id="1"]
+[ext_resource type="Script" uid="uid://btyrwps5h2qn7" path="res://addons/escoria-core/ui_library/menus/options/options.gd" id="4"]
+
+[node name="options" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("4")
+
+[node name="Panel" type="ColorRect" parent="."]
+layout_mode = 0
+anchor_right = 1.0
+anchor_bottom = 1.0
+color = Color(0.270588, 0.270588, 0.270588, 1)
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 0
+anchor_right = 1.0
+anchor_bottom = 1.0
+alignment = 1
+
+[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 6
+theme_override_constants/margin_left = 20
+theme_override_constants/margin_top = 20
+theme_override_constants/margin_right = 20
+theme_override_constants/margin_bottom = 20
+
+[node name="options" type="GridContainer" parent="VBoxContainer/MarginContainer"]
+layout_mode = 2
+size_flags_vertical = 6
+theme_override_constants/h_separation = 40
+columns = 2
+
+[node name="label" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "OPTIONS_LANGUAGE"
+
+[node name="flags" type="HBoxContainer" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+size_flags_vertical = 3
+theme_override_constants/separation = 30
+alignment = 1
+
+[node name="TextureRect2" type="TextureRect" parent="VBoxContainer/MarginContainer/options/flags"]
+layout_mode = 2
+texture = ExtResource("1")
+
+[node name="TextureRect3" type="TextureRect" parent="VBoxContainer/MarginContainer/options/flags"]
+layout_mode = 2
+texture = ExtResource("1")
+
+[node name="label2" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "GENERAL_VOLUME"
+
+[node name="general_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+size_flags_horizontal = 3
+max_value = 1.0
+step = 0.001
+
+[node name="label3" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "SOUND_VOLUME"
+
+[node name="sound_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+size_flags_horizontal = 3
+max_value = 1.0
+step = 0.001
+
+[node name="label4" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "MUSIC_VOLUME"
+
+[node name="music_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+size_flags_horizontal = 3
+max_value = 1.0
+step = 0.001
+
+[node name="label5" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "SPEECH_VOLUME"
+
+[node name="speech_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+size_flags_horizontal = 3
+max_value = 1.0
+step = 0.001
+
+[node name="label6" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "AMBIENT_VOLUME"
+
+[node name="ambient_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+size_flags_horizontal = 3
+max_value = 1.0
+step = 0.001
+
+[node name="label7" type="Label" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+text = "FULLSCREEN"
+
+[node name="fullscreen" type="CheckBox" parent="VBoxContainer/MarginContainer/options"]
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 20
+alignment = 1
+
+[node name="back" type="Button" parent="VBoxContainer/HBoxContainer"]
+layout_mode = 2
+text = "OPTIONS_BACK"
+
+[node name="apply" type="Button" parent="VBoxContainer/HBoxContainer"]
+layout_mode = 2
+text = "APPLY"
+
+[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/general_volume" to="." method="_on_general_volume_changed"]
+[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/sound_volume" to="." method="_on_sound_volume_changed"]
+[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/music_volume" to="." method="_on_music_volume_changed"]
+[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/speech_volume" to="." method="_on_speech_volume_value_changed"]
+[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/ambient_volume" to="." method="_on_ambient_volume_value_changed"]
+[connection signal="toggled" from="VBoxContainer/MarginContainer/options/fullscreen" to="." method="_on_fullscreen_toggled"]
+[connection signal="pressed" from="VBoxContainer/HBoxContainer/back" to="." method="_on_back_pressed"]
+[connection signal="pressed" from="VBoxContainer/HBoxContainer/apply" to="." method="_on_apply_pressed"]
diff --git a/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd
new file mode 100644
index 0000000..e4f9c66
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd
@@ -0,0 +1,75 @@
+# A menu shown in game
+extends Control
+
+
+# Make the pause menu process in pause mode and hide it just to be sure
+func _ready():
+ self.process_mode = Node.PROCESS_MODE_ALWAYS
+ hide()
+
+
+# Continue the game
+func _on_continue_pressed():
+ escoria.main.current_scene.game.unpause_game()
+
+
+# Show the save slots
+func _on_save_game_pressed():
+ $VBoxContainer.hide()
+ $save_game.refresh_savegames()
+ $save_game.show()
+
+
+# Show the load slots
+func _on_load_game_pressed():
+ $VBoxContainer.hide()
+ $load_game.refresh_savegames()
+ $load_game.show()
+
+
+# Show the options menu
+func _on_options_pressed():
+ $VBoxContainer.hide()
+ $options.show()
+
+
+# Quit the game
+func _on_quit_pressed():
+ escoria.quit()
+
+
+# Hide the save slots after clicking back button
+func _on_save_game_back_button_pressed():
+ reset()
+
+
+# Hide the load slots after clicking back button
+func _on_load_game_back_button_pressed():
+ reset()
+
+
+# Hide options menu after clicking back button
+func _on_options_back_button_pressed():
+ reset()
+
+
+# Set whether saving is enabled currently
+#
+# #### Parameters
+# - p_enabled: Enable or disable saving
+func set_save_enabled(p_enabled: bool):
+ $VBoxContainer/menuitems/save_game.disabled = !p_enabled
+
+
+# Resets the UI to initial state
+func reset():
+ $save_game.hide()
+ $load_game.hide()
+ $options.hide()
+ $VBoxContainer.show()
+
+
+func _on_new_game_pressed():
+ #yield(escoria.new_game(), "completed")
+ escoria.new_game()
+ escoria.main.current_scene.game.unpause_game()
diff --git a/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid
new file mode 100644
index 0000000..3f8bafb
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid
@@ -0,0 +1 @@
+uid://dvx5soxivafmw
diff --git a/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn
new file mode 100644
index 0000000..954191d
--- /dev/null
+++ b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn
@@ -0,0 +1,94 @@
+[gd_scene load_steps=7 format=3 uid="uid://gfjgmbcafyyt"]
+
+[ext_resource type="Script" uid="uid://dvx5soxivafmw" path="res://addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd" id="1"]
+[ext_resource type="PackedScene" uid="uid://bhcfnds3yy2yv" path="res://addons/escoria-core/ui_library/menus/options/options.tscn" id="2"]
+[ext_resource type="Texture2D" uid="uid://bwplh73qf2twk" path="res://addons/escoria-core/design/escoria-logo-small.png" id="3"]
+[ext_resource type="PackedScene" uid="uid://cj440t8pub603" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn" id="4"]
+[ext_resource type="PackedScene" uid="uid://j3wkbyhedkpr" path="res://addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn" id="5"]
+[ext_resource type="Theme" uid="uid://b081yyinnj03t" path="res://addons/escoria-core/ui_library/theme.tres" id="6"]
+
+[node name="pause_menu" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1")
+
+[node name="Panel" type="ColorRect" parent="."]
+layout_mode = 0
+anchor_right = 1.0
+anchor_bottom = 1.0
+color = Color(0.270588, 0.270588, 0.270588, 1)
+
+[node name="save_game" parent="." instance=ExtResource("4")]
+visible = false
+layout_mode = 1
+theme = ExtResource("6")
+
+[node name="load_game" parent="." instance=ExtResource("5")]
+visible = false
+layout_mode = 1
+
+[node name="options" parent="." instance=ExtResource("2")]
+visible = false
+layout_mode = 1
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 0
+anchor_left = 0.5
+anchor_right = 0.5
+anchor_bottom = 1.0
+offset_left = -308.0
+offset_right = 308.0
+theme_override_constants/separation = 100
+alignment = 1
+
+[node name="TextureRect" type="TextureRect" parent="VBoxContainer"]
+layout_mode = 2
+texture = ExtResource("3")
+
+[node name="menuitems" type="VBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = 10
+
+[node name="continue" type="Button" parent="VBoxContainer/menuitems"]
+custom_minimum_size = Vector2(0, 150)
+layout_mode = 2
+size_flags_vertical = 3
+text = "CONTINUE_GAME"
+
+[node name="new_game" type="Button" parent="VBoxContainer/menuitems"]
+layout_mode = 2
+size_flags_vertical = 3
+text = "NEW_GAME"
+
+[node name="save_game" type="Button" parent="VBoxContainer/menuitems"]
+layout_mode = 2
+size_flags_vertical = 3
+text = "SAVE_GAME"
+
+[node name="load_game" type="Button" parent="VBoxContainer/menuitems"]
+layout_mode = 2
+size_flags_vertical = 3
+text = "LOAD_GAME"
+
+[node name="options" type="Button" parent="VBoxContainer/menuitems"]
+layout_mode = 2
+text = "OPTIONS"
+
+[node name="quit" type="Button" parent="VBoxContainer/menuitems"]
+layout_mode = 2
+size_flags_vertical = 3
+text = "QUIT"
+
+[connection signal="back_button_pressed" from="save_game" to="." method="_on_save_game_back_button_pressed"]
+[connection signal="back_button_pressed" from="load_game" to="." method="_on_load_game_back_button_pressed"]
+[connection signal="back_button_pressed" from="options" to="." method="_on_options_back_button_pressed"]
+[connection signal="pressed" from="VBoxContainer/menuitems/continue" to="." method="_on_continue_pressed"]
+[connection signal="pressed" from="VBoxContainer/menuitems/new_game" to="." method="_on_new_game_pressed"]
+[connection signal="pressed" from="VBoxContainer/menuitems/save_game" to="." method="_on_save_game_pressed"]
+[connection signal="pressed" from="VBoxContainer/menuitems/load_game" to="." method="_on_load_game_pressed"]
+[connection signal="pressed" from="VBoxContainer/menuitems/options" to="." method="_on_options_pressed"]
+[connection signal="pressed" from="VBoxContainer/menuitems/quit" to="." method="_on_quit_pressed"]
diff --git a/addons/escoria-core/ui_library/theme.tres b/addons/escoria-core/ui_library/theme.tres
new file mode 100644
index 0000000..d21e9fd
--- /dev/null
+++ b/addons/escoria-core/ui_library/theme.tres
@@ -0,0 +1,6 @@
+[gd_resource type="Theme" load_steps=2 format=3 uid="uid://b081yyinnj03t"]
+
+[ext_resource type="FontFile" path="res://addons/escoria-core/ui_library/fonts/caslonantique.tres" id="1"]
+
+[resource]
+default_font = ExtResource("1")
diff --git a/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd
new file mode 100644
index 0000000..0443f91
--- /dev/null
+++ b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd
@@ -0,0 +1,14 @@
+extends CanvasLayer
+
+@onready var list = $VBoxContainer/hover_stack
+
+func update() -> void:
+ for e in list.get_children():
+ list.remove_child(e)
+ e.queue_free()
+ if escoria.inputs_manager.hover_stack.is_empty():
+ return
+ for e in escoria.inputs_manager.hover_stack.get_all():
+ var l = Label.new()
+ l.text = e.global_id
+ list.add_child(l)
diff --git a/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid
new file mode 100644
index 0000000..a06dc00
--- /dev/null
+++ b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid
@@ -0,0 +1 @@
+uid://csscdvd15atps
diff --git a/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn
new file mode 100644
index 0000000..f368ce7
--- /dev/null
+++ b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn
@@ -0,0 +1,23 @@
+[gd_scene load_steps=2 format=3 uid="uid://ceu6y33rrbdr"]
+
+[ext_resource type="Script" uid="uid://csscdvd15atps" path="res://addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd" id="1"]
+
+[node name="hover_stack_layer" type="CanvasLayer"]
+script = ExtResource("1")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+offset_top = 32.0
+offset_right = 167.0
+offset_bottom = 84.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="title" type="RichTextLabel" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+bbcode_enabled = true
+text = "Hover stack"
+
+[node name="hover_stack" type="VBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
diff --git a/addons/escoria-core/ui_library/tools/room_select/room_select.gd b/addons/escoria-core/ui_library/tools/room_select/room_select.gd
new file mode 100644
index 0000000..cc03b40
--- /dev/null
+++ b/addons/escoria-core/ui_library/tools/room_select/room_select.gd
@@ -0,0 +1,73 @@
+# A small utility to quickly switch to a room while developing
+extends OptionButton
+
+
+# The selected option
+var _selected_id = 0
+
+
+# The path to available rooms
+var _options_paths = []
+
+
+# Build up the list of rooms
+func _ready():
+ var rooms_folder = ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.ROOM_SELECTOR_ROOM_DIR
+ )
+ if rooms_folder == "" or \
+ not ESCProjectSettingsManager.get_setting(
+ ESCProjectSettingsManager.ENABLE_ROOM_SELECTOR
+ ):
+ return
+ var rooms_list: Array = []
+ var path = ProjectSettings.globalize_path(rooms_folder)
+ if not OS.has_feature("editor"):
+ path = OS.get_executable_path().get_base_dir().path_join(path)
+ var dir = DirAccess.open(path)
+ if dir != null:
+ dir.list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547
+ var file_name = dir.get_next()
+ while file_name != "":
+ if dir.current_is_dir():
+ rooms_list.push_back(file_name)
+ file_name = dir.get_next()
+
+ rooms_list.sort()
+ for room in rooms_list:
+ add_item(room)
+ _options_paths.push_back("%s/%s/%s.tscn" %[
+ rooms_folder,
+ room,
+ room
+ ])
+
+ else:
+ escoria.logger.warn(
+ self,
+ "A problem occurred while opening rooms folder %s." % str(path)
+ )
+
+
+# Switch to the selected room
+func _on_button_pressed():
+ # When next room is loaded, we don't want to consider ESC_LAST_SCENE for
+ # automatic transitions.
+ # If FORCE_LAST_SCENE_NULL is True when change_scene starts:
+ # - ESC_LAST_SCENE is set to empty
+ escoria.globals_manager.set_global(
+ escoria.room_manager.GLOBAL_FORCE_LAST_SCENE_NULL,
+ true,
+ true
+ )
+
+ escoria.event_manager.interrupt()
+ escoria.room_manager.change_scene_to_file(_options_paths[_selected_id], false)
+
+
+# A room was selected, store the selection
+#
+# #### Parameters
+# - index: The index of the selected room in the paths list
+func _on_option_item_selected(index):
+ _selected_id = index
diff --git a/addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid b/addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid
new file mode 100644
index 0000000..b36c503
--- /dev/null
+++ b/addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid
@@ -0,0 +1 @@
+uid://b5uyabsv4jdxn
diff --git a/addons/escoria-core/ui_library/tools/room_select/room_select.tscn b/addons/escoria-core/ui_library/tools/room_select/room_select.tscn
new file mode 100644
index 0000000..3feb0d5
--- /dev/null
+++ b/addons/escoria-core/ui_library/tools/room_select/room_select.tscn
@@ -0,0 +1,25 @@
+[gd_scene load_steps=2 format=3 uid="uid://c5xhvh8pw1qba"]
+
+[ext_resource type="Script" uid="uid://b5uyabsv4jdxn" path="res://addons/escoria-core/ui_library/tools/room_select/room_select.gd" id="1"]
+
+[node name="room_select" type="HBoxContainer"]
+offset_right = 63.0
+offset_bottom = 40.0005
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="option" type="OptionButton" parent="."]
+offset_right = 29.0
+offset_bottom = 40.0
+size_flags_horizontal = 3
+script = ExtResource("1")
+
+[node name="button" type="Button" parent="."]
+offset_left = 33.0
+offset_right = 63.0
+offset_bottom = 40.0
+text = "Go"
+
+[connection signal="item_selected" from="option" to="option" method="_on_option_item_selected"]
+[connection signal="pressed" from="button" to="option" method="_on_button_pressed"]