This commit is contained in:
2026-02-23 11:37:27 +01:00
commit 13dbb551c8
94 changed files with 2682 additions and 0 deletions

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,69 @@
[docks]
dock_3_selected_tab_idx=0
dock_4_selected_tab_idx=0
dock_5_selected_tab_idx=0
dock_floating={}
dock_closed=[]
dock_split_2=0
dock_split_3=0
dock_hsplit_1=0
dock_hsplit_2=280
dock_hsplit_3=-280
dock_hsplit_4=0
dock_3="Scene,Import"
dock_4="FileSystem,History"
dock_5="Inspector,Signals,Groups"
dock_9="Output,Debugger,Audio,Animation,Shader Editor,Search Results,AnimationTree,ResourcePreloader,ShaderFile,SpriteFrames,Theme,Polygon,TileSet,TileMap,Replication,GridMap"
[docks/FileSystem]
h_split_offset=240
v_split_offset=0
display_mode=0
file_sort=0
file_list_display_mode=1
selected_paths=PackedStringArray("res://Machines/")
uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://ui/screens/", "res://ui/screens/title-screen/")
[docks/History]
include_scene=true
include_global=true
[EditorNode]
open_scenes=PackedStringArray("res://core/gamemanager.tscn", "res://levels/main_level.tscn", "res://ui/screens/title-screen/title_screen.tscn")
current_scene="res://ui/screens/title-screen/title_screen.tscn"
bottom_panel_offsets={
"Audio": -450
}
selected_default_debugger_tab_idx=0
selected_main_editor_idx=2
[EditorWindow]
screen=0
mode="maximized"
position=Vector2i(1986, 69)
[ScriptEditor]
open_scripts=["res://Machines/machine_resource.gd"]
selected_script="res://Machines/machine_resource.gd"
open_help=[]
script_split_offset=200
list_split_offset=0
zoom_factor=1.0
[GameView]
floating_window_rect=Rect2i(0, 0, 0, 0)
floating_window_screen=-1
[ShaderEditor]
open_shaders=[]
split_offset=200
selected_shader=""
text_shader_zoom_factor=1.0

View File

@@ -0,0 +1,3 @@
[folding]
sections_unfolded=PackedStringArray()

View File

@@ -0,0 +1,58 @@
63f7b34db8d8cdea90c76aacccf841ec
::res://::1771841836
icon.svg::CompressedTexture2D::7874505655436279264::1771841784::1771841837::1::::<><><>0<>0<>8c6dc71835cb1092831a2181a7cbcbe4<>res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex::
README.md::TextFile::-1::1771841784::0::1::::<><><>0<>0<><>::
::res://autoloads/::1771841784
debug_global.gd::GDScript::3519651180120881073::1771841784::0::1::::<>Node<><>0<>0<><>::
global.gd::GDScript::1176894133255043534::1771841784::0::1::::<>Node<><>0<>0<><>::
input_manager.gd::GDScript::8575146745769188893::1771841784::0::1::::<>Node<><>0<>0<><>::
::res://autoloads/settings/::1771841784
settings.gd::GDScript::1169167139487781899::1771841784::0::1::::<>Node<><>0<>0<><>::
user_defined_settings.gd::GDScript::5353537388395803011::1771841784::0::1::::UserDefinedSettings<>RefCounted<><>0<>0<><>::
::res://core/::1771841784
default_bus_layout.tres::AudioBusLayout::6406833063048569073::1771841784::0::1::::<><><>0<>0<><>::
gamemanager.gd::GDScript::5587465302030124915::1771841784::0::1::::GameManager<>Node<><>0<>0<><>::
gamemanager.tscn::PackedScene::5236817490969193782::1771841784::0::1::::<><><>0<>0<><>::uid://cluu0cgltsenj::::res://core/gamemanager.gd<>uid://bilai15byqef2::::res://ui/screens/pause-menu/pause_menu.tscn<>uid://bhb4ckqipjvu3::::res://core/main_environment.tres<>uid://cyfvcuxi210mg::::res://levels/main_level.tscn<>uid://hheneshfv1b2::::res://ui/themes/your_theme.tres
main_environment.tres::Environment::2881571881625704016::1771841784::0::1::::<><><>0<>0<><>::
::res://levels/::1771841784
levels_readme.md::TextFile::-1::1771841784::0::1::::<><><>0<>0<><>::
main_level.tscn::PackedScene::6468975218303303874::1771841784::0::1::::<><><>0<>0<><>::
::res://ui/::1771841784
::res://ui/components/::1771841784
::res://ui/components/settings-menu/::1771841784
Revert.svg::CompressedTexture2D::4339617245426587863::1771841784::1771841837::1::::<><><>0<>0<>297dc219980ee096674b7de235f79953<>res://.godot/imported/Revert.svg-83732f7446608123e95cc2f2a2cf35c6.ctex::
settings_menu.gd::GDScript::582134994019510841::1771841784::0::1::::<>TabContainer<><>0<>0<><>::
settings_menu.tscn::PackedScene::6304534932307647668::1771841784::0::1::::<><><>0<>0<><>::uid://ij8xcd6np4vt::::res://ui/components/settings-menu/settings_menu.gd<>uid://hheneshfv1b2::::res://ui/themes/your_theme.tres
::res://ui/components/settings-menu/action-remapping-button/::1771841784
action_remapping_button.gd::GDScript::4642898967767345029::1771841784::0::1::::<>Button<><>0<>0<><>::
action_remapping_button.tscn::PackedScene::925410865498412070::1771841784::0::1::::<><><>0<>0<><>::uid://b7fb4ravo24q0::::res://ui/components/settings-menu/action-remapping-button/action_remapping_button.gd
::res://ui/screens/::1771841784
::res://ui/screens/control-screen/::1771841784
control_screen.gd::GDScript::6615828920758056178::1771841784::0::1::::<>Control<><>0<>0<><>::
control_screen.tscn::PackedScene::3476236442146173568::1771841784::0::1::::<><><>0<>0<><>::uid://hheneshfv1b2::::res://ui/themes/your_theme.tres<>uid://c1i00arsdf2mw::::res://ui/screens/control-screen/control_screen.gd
::res://ui/screens/credit-screen/::1771841784
credits.json::JSON::-1::1771841784::0::1::::<><><>0<>0<><>::
credit_element.gd::GDScript::2210728914878077746::1771841784::0::1::::CreditButton<>VBoxContainer<><>0<>0<><>::
credit_element.tscn::PackedScene::2912162567317968620::1771841784::0::1::::<><><>0<>0<><>::uid://6q5m84onxilu::::res://ui/screens/credit-screen/credit_element.gd
credit_entry.gd::GDScript::5420422509298114093::1771841784::0::1::::CreditEntry<>Resource<><>0<>0<><>::
credit_screen.gd::GDScript::8836023702103504430::1771841784::0::1::::<>Control<><>0<>0<><>::
credit_screen.tscn::PackedScene::358879150997730698::1771841784::0::1::::<><><>0<>0<><>::uid://dx5iawpcgwhlm::::res://ui/screens/credit-screen/credit_screen.gd<>uid://hheneshfv1b2::::res://ui/themes/your_theme.tres
::res://ui/screens/level-select-screen/::1771841784
level_button.gd::GDScript::8704749015755386232::1771841784::0::1::::LevelButton<>Button<><>0<>0<><>::
level_button.tscn::PackedScene::4949170022177206724::1771841784::0::1::::<><><>0<>0<><>::uid://dwav7d4amw8yu::::res://ui/screens/level-select-screen/level_button.gd
level_select.gd::GDScript::9072899083889100288::1771841784::0::1::::<>Control<><>0<>0<><>::
level_select.tscn::PackedScene::6316590214924971141::1771841784::0::1::::<><><>0<>0<><>::uid://hheneshfv1b2::::res://ui/themes/your_theme.tres<>uid://d2i8k7votngvg::::res://ui/screens/level-select-screen/level_select.gd
::res://ui/screens/pause-menu/::1771841784
pause_menu.gd::GDScript::1923905240375656616::1771841784::0::1::::<>Control<><>0<>0<><>::
pause_menu.tscn::PackedScene::2970655286472634229::1771841784::0::1::::<><><>0<>0<><>::uid://hheneshfv1b2::::res://ui/themes/your_theme.tres<>uid://2n7o5t6b5g4w::::res://ui/screens/pause-menu/pause_menu.gd<>uid://cv271fh4d2p13::::res://ui/components/settings-menu/settings_menu.tscn
::res://ui/screens/settings-screen/::1771841784
settings_screen.gd::GDScript::1222641183840346394::1771841784::0::1::::<>MarginContainer<><>0<>0<><>::
settings_screen.tscn::PackedScene::1731407975515257069::1771841784::0::1::::<><><>0<>0<><>::uid://hheneshfv1b2::::res://ui/themes/your_theme.tres<>uid://roiysiotwsbq::::res://ui/screens/settings-screen/settings_screen.gd<>uid://cv271fh4d2p13::::res://ui/components/settings-menu/settings_menu.tscn
::res://ui/screens/title-screen/::1771841784
title_screen.gd::GDScript::1850264627441223832::1771841784::0::1::::<>Control<><>0<>0<><>::
title_screen.tscn::PackedScene::7375686277331198226::1771841784::0::1::::<><><>0<>0<><>::uid://hheneshfv1b2::::res://ui/themes/your_theme.tres<>uid://1mjt83fygiua::::res://ui/screens/title-screen/title_screen.gd
::res://ui/screens/win-screen/::1771841784
win_screen.gd::GDScript::7620355351831359998::1771841784::0::1::::<>Control<><>0<>0<><>::
win_screen.tscn::PackedScene::477294899242648607::1771841784::0::1::::<><><>0<>0<><>::uid://dgtme0a6lym5c::::res://ui/screens/win-screen/win_screen.gd
::res://ui/themes/::1771841784
your_theme.tres::Theme::506038871261939837::1771841784::0::1::::<><><>0<>0<><>::uid://b23e4kqj4o6dv::::res://ui/components/settings-menu/Revert.svg

View File

@@ -0,0 +1,4 @@
res://ui/screens/title-screen/title_screen.tscn
res://ui/themes/your_theme.tres
res://core/main_environment.tres
res://Machines/machine_resource.gd

View File

@@ -0,0 +1,5 @@
[folding]
node_unfolds=[NodePath("."), PackedStringArray("Process"), NodePath("MenuLayer/DebugLabel"), PackedStringArray("Visibility", "Layout", "Theme"), NodePath("MenuLayer/PauseMenu"), PackedStringArray("Visibility", "Layout", "Theme")]
resource_unfolds=[]
nodes_folded=[]

View File

@@ -0,0 +1,3 @@
[folding]
sections_unfolded=PackedStringArray()

View File

@@ -0,0 +1,3 @@
[folding]
sections_unfolded=PackedStringArray()

View File

@@ -0,0 +1,5 @@
[folding]
node_unfolds=[]
resource_unfolds=[]
nodes_folded=[]

View File

@@ -0,0 +1,16 @@
[game_view]
select_mode=0
[editor_metadata]
executable_path="/home/emily/Desktop/Godot_v4.5.1-stable_mono_linux_x86_64/Godot_v4.6-stable_linux.x86_64"
[recent_files]
scenes=["res://ui/screens/title-screen/title_screen.tscn", "res://levels/main_level.tscn", "res://core/gamemanager.tscn"]
scripts=["res://Machines/machine_resource.gd"]
[script_setup]
last_selected_language="GDScript"

View File

@@ -0,0 +1,17 @@
[res://Machines/machine_resource.gd]
state={
"bookmarks": PackedInt32Array(),
"breakpoints": PackedInt32Array(),
"column": 0,
"folded_lines": PackedInt32Array(),
"h_scroll_position": 0,
"row": 0,
"scroll_position": 0.0,
"selection": true,
"selection_from_column": 0,
"selection_from_line": 0,
"selection_to_column": 0,
"selection_to_line": 10,
"syntax_highlighter": "GDScript"
}

View File

@@ -0,0 +1,192 @@
[editor_states]
2D={
"grid_offset": Vector2(0, 0),
"grid_snap_active": false,
"grid_step": Vector2(8, 8),
"grid_visibility": 1,
"ofs": Vector2(-166, -111),
"primary_grid_step": Vector2i(8, 8),
"show_group_gizmos": true,
"show_guides": true,
"show_helpers": false,
"show_lock_gizmos": true,
"show_origin": true,
"show_position_gizmos": true,
"show_rulers": true,
"show_transformation_gizmos": true,
"show_viewport": true,
"show_zoom_control": true,
"smart_snap_active": false,
"snap_guides": true,
"snap_node_anchors": true,
"snap_node_center": true,
"snap_node_parent": true,
"snap_node_sides": true,
"snap_other_nodes": true,
"snap_pixel": true,
"snap_relative": false,
"snap_rotation": false,
"snap_rotation_offset": 0.0,
"snap_rotation_step": 0.2617994,
"snap_scale": false,
"snap_scale_step": 0.1,
"zoom": 1.0
}
3D={
"fov": 70.01,
"gizmos_status": {
"AudioListener3D": 0,
"AudioStreamPlayer3D": 0,
"CPUParticles3D": 0,
"CSGShape3D": 0,
"Camera3D": 0,
"ChainIK3D": 0,
"CollisionObject3D": 0,
"CollisionPolygon3D": 0,
"CollisionShape3D": 0,
"Decal": 0,
"FogVolume": 0,
"GPUParticles3D": 0,
"GPUParticlesCollision3D": 0,
"Joint3D": 0,
"Light3D": 0,
"LightmapGI": 0,
"LightmapProbe": 0,
"Marker3D": 0,
"MeshInstance3DCustomAABB": 0,
"NavigationLink3D": 0,
"NavigationObstacle3D": 0,
"NavigationRegion3D": 0,
"OccluderInstance3D": 0,
"Particles3DEmissionShape": 0,
"Path3D": 0,
"PhysicalBone3D": 0,
"RayCast3D": 0,
"ReflectionProbe": 0,
"ShapeCast3D": 0,
"Skeleton3D": 0,
"SoftBody3D": 0,
"SpringArm3D": 0,
"SpringBoneCollision3D": 0,
"SpringBoneSimulator3D": 0,
"TwoBoneIK3D": 0,
"VehicleWheel3D": 0,
"VisibleOnScreenNotifier3D": 0,
"VoxelGI": 0
},
"local_coords": false,
"preview_sun_env": {
"environ_ao_enabled": false,
"environ_enabled": true,
"environ_energy": 1.0,
"environ_gi_enabled": false,
"environ_glow_enabled": false,
"environ_ground_color": Color(0.2, 0.169, 0.133, 1),
"environ_sky_color": Color(0.385, 0.454, 0.55, 1),
"environ_tonemap_enabled": true,
"sun_color": Color(1, 1, 1, 1),
"sun_enabled": true,
"sun_energy": 1.0,
"sun_rotation": Vector2(-1.0471976, 2.6179938),
"sun_shadow_max_distance": 100.0
},
"rotate_snap": 15.0,
"scale_snap": 10.0,
"show_grid": true,
"show_origin": true,
"snap_enabled": false,
"translate_snap": 1.0,
"viewport_mode": 1,
"viewports": [{
"auto_orthogonal": false,
"auto_orthogonal_enabled": true,
"cinematic_preview": false,
"display_mode": 22,
"distance": 4.0,
"doppler": false,
"frame_time": false,
"gizmos": true,
"grid": true,
"half_res": false,
"information": false,
"listener": true,
"lock_rotation": false,
"orthogonal": false,
"position": Vector3(0, 0, 0),
"transform_gizmo": true,
"use_environment": false,
"view_type": 0,
"x_rotation": 0.5,
"y_rotation": -0.5
}, {
"auto_orthogonal": false,
"auto_orthogonal_enabled": true,
"cinematic_preview": false,
"display_mode": 22,
"distance": 4.0,
"doppler": false,
"frame_time": false,
"gizmos": true,
"grid": true,
"half_res": false,
"information": false,
"listener": false,
"lock_rotation": false,
"orthogonal": false,
"position": Vector3(0, 0, 0),
"transform_gizmo": true,
"use_environment": false,
"view_type": 0,
"x_rotation": 0.5,
"y_rotation": -0.5
}, {
"auto_orthogonal": false,
"auto_orthogonal_enabled": true,
"cinematic_preview": false,
"display_mode": 22,
"distance": 4.0,
"doppler": false,
"frame_time": false,
"gizmos": true,
"grid": true,
"half_res": false,
"information": false,
"listener": false,
"lock_rotation": false,
"orthogonal": false,
"position": Vector3(0, 0, 0),
"transform_gizmo": true,
"use_environment": false,
"view_type": 0,
"x_rotation": 0.5,
"y_rotation": -0.5
}, {
"auto_orthogonal": false,
"auto_orthogonal_enabled": true,
"cinematic_preview": false,
"display_mode": 22,
"distance": 4.0,
"doppler": false,
"frame_time": false,
"gizmos": true,
"grid": true,
"half_res": false,
"information": false,
"listener": false,
"lock_rotation": false,
"orthogonal": false,
"position": Vector3(0, 0, 0),
"transform_gizmo": true,
"use_environment": false,
"view_type": 0,
"x_rotation": 0.5,
"y_rotation": -0.5
}],
"zfar": 4000.01,
"znear": 0.05
}
Anim={
"visible": false
}
selected_nodes=Array[NodePath]([])

View File

@@ -0,0 +1,5 @@
[folding]
node_unfolds=[NodePath("."), PackedStringArray("Layout", "Theme"), NodePath("CenterContainer"), PackedStringArray("Layout"), NodePath("CenterContainer/VBoxContainer"), PackedStringArray("Layout"), NodePath("CenterContainer/VBoxContainer/Label"), PackedStringArray("Layout"), NodePath("CenterContainer/VBoxContainer/Label2"), PackedStringArray("Layout"), NodePath("CenterContainer2"), PackedStringArray("Layout"), NodePath("CenterContainer2/VBoxContainer"), PackedStringArray("Layout"), NodePath("CenterContainer2/VBoxContainer/Start"), PackedStringArray("Layout", "Theme Overrides"), NodePath("CenterContainer2/VBoxContainer/LevelSelect"), PackedStringArray("Layout", "Theme Overrides"), NodePath("CenterContainer2/VBoxContainer/Options"), PackedStringArray("Layout", "Theme Overrides"), NodePath("CenterContainer2/VBoxContainer/Credit"), PackedStringArray("Layout", "Theme Overrides"), NodePath("CenterContainer2/VBoxContainer/Quit"), PackedStringArray("Layout", "Theme Overrides"), NodePath("Label"), PackedStringArray("Layout")]
resource_unfolds=["res://ui/screens/title-screen/title_screen.tscn::LabelSettings_s0ue6", PackedStringArray("Resource", "Font"), "res://ui/screens/title-screen/title_screen.tscn::LabelSettings_73xnf", PackedStringArray("Resource", "Font"), "res://ui/screens/title-screen/title_screen.tscn::LabelSettings_wly7t", PackedStringArray("Resource", "Font")]
nodes_folded=[]

View File

@@ -0,0 +1,3 @@
[folding]
sections_unfolded=PackedStringArray()

View File

@@ -0,0 +1,49 @@
list=[{
"base": &"Resource",
"class": &"machine",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://Machines/machine_resource.gd"
}, {
"base": &"Button",
"class": &"LevelButton",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://ui/screens/level-select-screen/level_button.gd"
}, {
"base": &"Resource",
"class": &"CreditEntry",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://ui/screens/credit-screen/credit_entry.gd"
}, {
"base": &"VBoxContainer",
"class": &"CreditButton",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://ui/screens/credit-screen/credit_element.gd"
}, {
"base": &"Node",
"class": &"GameManager",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://core/gamemanager.gd"
}, {
"base": &"RefCounted",
"class": &"UserDefinedSettings",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://autoloads/settings/user_defined_settings.gd"
}]

View File

@@ -0,0 +1,3 @@
source_md5="74b418f0ad54c53aeb91140a4206ecb3"
dest_md5="e98449c33f4839c7955d565269d6f1fc"

View File

@@ -0,0 +1,3 @@
source_md5="0eed98577cbbf02f0bdc0f5c9f70465b"
dest_md5="b48bef1f2eefdd190e0a3e60a6188d75"

Binary file not shown.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Ekaterina Ehringhaus and Hendrik Brucker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,10 @@
extends Resource
class_name machine
@export var modell_name : StringName
@export var carry_capacity : float
@export var hit_points : float
@export var fuel_tank_capacity : float
@export var weapon_slots : Array
@export var machine_upgrades : Array
var current_fuel_in_tank : float

View File

@@ -0,0 +1 @@
uid://dyur6uu5di5ak

View File

@@ -0,0 +1,43 @@
extends Node
var debug_label : Label
var debug_dict = Dictionary()
var update_queued = true
func _ready() -> void:
InputManager.game_debug_show.connect(_show_label)
InputManager.game_debug_hide.connect(_hide_label)
func _show_label() -> void:
if debug_label:
debug_label.visible = true
InputManager.is_debug_label_visible = true
if debug_label == null:
push_warning("debug label not set")
func _hide_label() -> void:
if debug_label:
debug_label.visible = false
InputManager.is_debug_label_visible = false
func set_debug_info(key: String, value: Variant):
debug_dict[key] = value
update_queued = true
func reset_debug_info(key: String):
debug_dict.erase(key)
update_queued = true
func _process(_delta):
if update_queued:
if debug_label == null:
update_queued = false
return
var debug_text = ""
for key in debug_dict.keys():
debug_text += key + ": " + str(debug_dict[key]) + "\n"
debug_label.text = debug_text
update_queued = false

View File

@@ -0,0 +1 @@
uid://bqe7e1wowo03f

View File

@@ -0,0 +1,6 @@
extends Node
var game_manager : GameManager = null
func set_game_manager(p_game_manager: GameManager):
game_manager = p_game_manager

View File

@@ -0,0 +1 @@
uid://q1djliepls3g

View File

@@ -0,0 +1,50 @@
extends Node
#region Showing new Screens
signal game_pause
signal game_unpause
signal game_debug_show
signal game_debug_hide
#endregion
#region Game State Management
var _is_in_game: bool = false
var _is_paused: bool = false
var is_debug_label_visible: bool = false
var capture_mouse_ingame: bool = true
#endregion
enum InputType {MOUSE, JOYPAD}
func _ready():
set_process_mode(Node.PROCESS_MODE_ALWAYS)
func _input(event: InputEvent) -> void:
if event.is_action_pressed("pause"):
if (_is_in_game and not _is_paused):
game_pause.emit()
elif (_is_in_game and _is_paused):
game_unpause.emit()
elif event.is_action_pressed("toggle_debug_label"):
if is_debug_label_visible:
game_debug_hide.emit()
else:
game_debug_show.emit()
elif not _is_in_game:
pass
func set_is_in_game(b: bool) -> void:
_is_in_game = b
_update_mouse_capture()
func _update_mouse_capture() -> void:
if capture_mouse_ingame:
if (not _is_in_game) || _is_paused:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func set_is_paused(b: bool) -> void:
_is_paused = b
_update_mouse_capture()

View File

@@ -0,0 +1 @@
uid://duf4ocwe44jb6

View File

@@ -0,0 +1,321 @@
extends Node
#region Helper enums and data classes
class SettingCategory:
var name: String
var display_text: String
var sub_categories: Array[SettingCategory] = []
var settings: Array[Setting] = []
func _init(p_name: String, p_display_text: String = ""):
name = p_name
display_text = p_display_text if p_display_text != "" else p_name
func add_setting(p_setting: Setting) -> void:
if OS.is_debug_build() and settings.filter(func(s): return s.name == p_setting.name).size() > 0:
push_warning("Setting " + p_setting.name + " already exists in category " + name)
return
settings.append(p_setting)
func add_sub_category(p_category: SettingCategory) -> void:
if OS.is_debug_build() and sub_categories.filter(func(c): return c.name == p_category.name).size() > 0:
push_warning("Sub-category " + p_category.name + " already exists in category " + name)
return
sub_categories.append(p_category)
func get_setting(p_name: String) -> Setting:
for setting in settings:
if setting.name == p_name:
return setting
push_warning("Setting " + p_name + " not found in category " + name)
return null
func get_sub_category(p_name: String) -> SettingCategory:
for category in sub_categories:
if category.name == p_name:
return category
push_warning("Sub-category " + p_name + " not found in category " + name)
return null
@abstract
class Setting:
signal value_changed(value: Variant)
var name: String
var display_text: String
var value: Variant:
set = set_value
var default_value: Variant
var requires_restart: bool
var setter_callable: Callable
func _init(p_name: String, p_display_text: String, p_default_value: Variant, p_setter_callable: Callable = Callable(), p_requires_restart: bool = false):
self.name = p_name
self.display_text = p_display_text
self.value = p_default_value
self.default_value = p_default_value
self.setter_callable = p_setter_callable
self.requires_restart = p_requires_restart
func set_value(p_value: Variant):
if not is_same(p_value, value):
value = p_value
value_changed.emit(value)
if setter_callable.is_valid():
setter_callable.call(value)
class OptionSetting extends Setting:
var options: Array[String]
func _init(p_name: String, p_label_text: String, p_default_value: String, p_options: Array[String], p_setter_callable: Callable = Callable(), p_requires_restart: bool = false):
assert(p_options.has(p_default_value))
self.options = p_options
super(p_name, p_label_text, p_default_value, p_setter_callable, p_requires_restart)
class FloatSetting extends Setting:
var min_value: float
var max_value: float
var step: float
func _init(p_name: String, p_label_text: String, p_default_value: float, p_min: float, p_max: float, p_step: float, p_setter_callable: Callable = Callable(), p_requires_restart: bool = false):
assert(p_default_value >= p_min and p_default_value <= p_max)
self.min_value = p_min
self.max_value = p_max
self.step = p_step
super(p_name, p_label_text, p_default_value, p_setter_callable, p_requires_restart)
class BoolSetting extends Setting:
func _init(p_name: String, p_label_text: String, p_default_value: bool, p_setter_callable: Callable = Callable(), p_requires_restart: bool = false):
super(p_name, p_label_text, p_default_value, p_setter_callable, p_requires_restart)
class InputRemappingSetting extends Setting:
var actions: Array[InputEvent]
func _init(p_name: String, p_label_text: String, p_default_value: Array[InputEvent], p_setter_callable: Callable = Callable(), p_requires_restart: bool = false):
super(p_name, p_label_text, p_default_value, p_setter_callable, p_requires_restart)
self.actions = p_default_value.duplicate()
#endregion
#region Settings interface
## Stores all settings in a hierarchical structure
var _root_categories: Array[SettingCategory] = []
## Adds a new root category which can be populated with settings and sub-_root_categories.
func add_root_category(p_setting_category: SettingCategory) -> void:
if _root_categories.filter(func(c): return c.name == p_setting_category.name).size() > 0:
push_warning("Category " + p_setting_category.name + " does already exist")
return
_root_categories.append(p_setting_category)
func get_root_categories() -> Array[SettingCategory]:
return _root_categories
func get_root_category(p_name: String) -> SettingCategory:
for category in _root_categories:
if category.name == p_name:
return category
push_warning("Category " + p_name + " not found")
return null
## Gets the value of a setting by path (e.g., "Category/SubCategory/SettingName")
func get_value(p_path: String) -> Variant:
var setting = _get_setting_by_path(p_path)
if setting:
return setting.value
push_warning("Setting " + p_path + " not found")
return null
## Sets the value of a setting by path (e.g., "Category/SubCategory/SettingName")
func set_value(p_path: String, p_value: Variant) -> void:
var setting = _get_setting_by_path(p_path)
if setting:
if typeof(p_value) != typeof(setting.default_value):
push_warning("Variant type mismatch for setting " + p_path + " (" + type_string(typeof(p_value)) + " should be " + type_string(typeof(setting.default_value)) + ")")
return
setting.value = p_value
else:
push_warning("Setting " + p_path + " not found")
## Sets the default value of a setting by path (e.g., "Category/SubCategory/SettingName")
func set_default_value(p_path: String, p_default_value: Variant) -> void:
var setting = _get_setting_by_path(p_path)
if setting:
if typeof(p_default_value) != typeof(setting.default_value):
push_warning("Variant type mismatch for setting " + p_path + " (" + type_string(typeof(p_default_value)) + " should be " + type_string(typeof(setting.default_value)) + ")")
return
setting.default_value = p_default_value
else:
push_warning("Setting " + p_path + " not found")
## Resets a setting to its default value by path (e.g., "Category/SubCategory/SettingName")
func reset_value(p_path: String):
var setting = _get_setting_by_path(p_path)
if setting:
setting.value = setting.default_value
if setting.setter_callable.is_valid():
setting.setter_callable.call(setting.default_value)
else:
push_warning("Setting " + p_path + " not found")
## Resets a specific input binding at the given index to its default value
func reset_input_binding(p_path: String, index: int):
var setting = _get_setting_by_path(p_path)
if not setting:
push_warning("Setting " + p_path + " not found")
return
if not setting is InputRemappingSetting:
push_warning("Setting " + p_path + " is not an InputRemappingSetting")
return
var events = setting.value.duplicate() as Array[InputEvent]
if index >= events.size():
push_warning("Index " + str(index) + " out of bounds for input_binding setting " + p_path)
return
var default_event = setting.default_value[index] if index < setting.default_value.size() else null
events[index] = default_event
setting.value = events
if setting.setter_callable.is_valid():
setting.setter_callable.call(events)
## Checks if a setting's value equals its default value
func is_value_default(p_path: String) -> bool:
var setting = _get_setting_by_path(p_path)
if setting:
return is_equal(setting.value, setting.default_value)
push_warning("Setting " + p_path + " not found")
return false
## Checks if a specific input binding at the given index equals its default value
func is_input_binding_default(p_path: String, index: int) -> bool:
var setting = _get_setting_by_path(p_path)
if not setting:
push_warning("Setting " + p_path + " not found")
return false
if not setting is InputRemappingSetting:
push_warning("Setting " + p_path + " is not an InputRemappingSetting")
return false
var event = setting.value[index] if index < setting.value.size() else null
var default_event = setting.default_value[index] if index < setting.default_value.size() else null
return is_equal(event, default_event)
## Helper function to find a setting by path (e.g., "Category/SubCategory/SettingName")
func _get_setting_by_path(p_path: String) -> Setting:
var path_parts = p_path.split("/")
if path_parts.size() < 2:
push_warning("Invalid setting path: " + p_path + ". Expected format: 'Category/SettingName' or 'Category/SubCategory/.../SettingName'")
return null
# Find the root category
var category = get_root_category(path_parts[0])
if not category:
return null
# Traverse subcategories
for i in range(1, path_parts.size() - 1):
category = category.get_sub_category(path_parts[i])
if not category:
return null
# Get the setting from the final category
var setting_name = path_parts[path_parts.size() - 1]
return category.get_setting(setting_name)
func save_config():
var config = ConfigFile.new()
for category in _root_categories:
_save_category_recursive(config, category, category.name)
var error = config.save("user://config.cfg")
if error != OK:
push_error("Failed to save config file: " + str(error))
## Recursively saves all settings in a category and its subcategories
func _save_category_recursive(config: ConfigFile, category: SettingCategory, section_path: String) -> void:
# Save all settings in this category
for setting in category.settings:
config.set_value(section_path, setting.name, setting.value)
# Recursively save all subcategories
for sub_category in category.sub_categories:
var sub_section_path = section_path + "/" + sub_category.name
_save_category_recursive(config, sub_category, sub_section_path)
func load_config():
var config = ConfigFile.new()
var error = config.load("user://config.cfg")
if error != OK:
push_warning("Failed to load config file: " + str(error) + ". Using default values.")
return
for category in _root_categories:
_load_category_recursive(config, category, category.name)
## Recursively loads all settings in a category and its subcategories
func _load_category_recursive(config: ConfigFile, category: SettingCategory, section_path: String) -> void:
# Load all settings in this category
for setting in category.settings:
if not config.has_section_key(section_path, setting.name):
push_warning("Setting " + setting.name + " not found in section " + section_path + ", using default value")
continue
var value = config.get_value(section_path, setting.name, setting.default_value)
if typeof(value) != typeof(setting.default_value):
push_warning("Variant type mismatch for setting " + setting.name + " (" + type_string(typeof(value)) + " should be " + type_string(typeof(setting.default_value)) + "), using default value")
continue
# TODO: Do something about this?
# Set the value (this will trigger the setter and emit signals)
setting.value = value
# Recursively load all subcategories
for sub_category in category.sub_categories:
var sub_section_path = section_path + "/" + sub_category.name
_load_category_recursive(config, sub_category, sub_section_path)
## Be cautious! Clears the config file, resetting all settings to their default values.
## This can be useful for troubleshooting.
func clear_config():
var config = ConfigFile.new()
config.save("user://config.cfg")
#endregion
## Deep property based equality check for two variants
func is_equal(a: Variant, b: Variant) -> bool:
if typeof(a) != typeof(b):
return false
if typeof(a) != TYPE_DICTIONARY and typeof(a) != TYPE_OBJECT and typeof(a) != TYPE_ARRAY:
return a == b
if typeof(a) == TYPE_ARRAY:
if a.size() != b.size():
return false
for i in range(a.size()):
if not is_equal(a[i], b[i]):
return false
return true
if typeof(a) == TYPE_DICTIONARY:
# Compare all keys of the two dictionaries
for key in a.keys():
if not b.has(key):
return false
if not is_equal(a[key], b[key]):
return false
for key in b.keys():
if not a.has(key):
return false
return true
# Both are objects, compare all properties
for prop in a.get_property_list():
if not is_equal(a.get(prop.name), b.get(prop.name)):
return false
return true

View File

@@ -0,0 +1 @@
uid://qwman3p22bxr

View File

@@ -0,0 +1,196 @@
class_name UserDefinedSettings
enum DisplayMode {FULLSCREEN, WINDOWED_FULLSCREEN, WINDOWED}
enum AAMode_3D {DISABLED, FXAA, TAA, MSAA_2X, MSAA_4X, FSR_2}
enum AAMode_2D {DISABLED, MSAA_2X, MSAA_4X, MSAA_8X}
enum AudioBus {MASTER, MUSIC, SFX, VOICE}
func _register_settings() -> void:
_register_graphics_settings()
_register_audio_settings()
_register_controls_settings()
func _register_graphics_settings() -> void:
var graphics_category = Settings.SettingCategory.new("graphics", "Graphics")
var display_mode_options: Array[String] = ["Fullscreen", "Windowed Fullscreen", "Windowed"]
var display_mode_setting = Settings.OptionSetting.new("display_mode",
"Display Mode",
"Windowed Fullscreen",
display_mode_options,
_set_display_mode)
graphics_category.add_setting(display_mode_setting)
var vsync_enabled_setting = Settings.BoolSetting.new("vsync_enabled", "VSync enabled", true, _set_vsync_enabled)
graphics_category.add_setting(vsync_enabled_setting)
var aa_3d_options: Array[String] = ["Disabled", "FXAA", "TAA", "MSAA 2x", "MSAA 4x", "FSR 2"]
var aa_3d_setting = Settings.OptionSetting.new("3d_aa_mode",
"3D Anti-Aliasing Mode",
"Disabled",
aa_3d_options,
_set_aa_mode_3d)
graphics_category.add_setting(aa_3d_setting)
# 2D MSAA is not yet available in the Compatibility renderer
if (ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method") != "gl_compatibility"):
var aa_2d_options: Array[String] = ["Disabled", "MSAA 2x", "MSAA 4x", "MSAA 8x"]
var aa_2d_setting = Settings.OptionSetting.new("2d_aa_mode",
"2D Anti-Aliasing Mode",
"Disabled",
aa_2d_options,
_set_aa_mode_2d)
graphics_category.add_setting(aa_2d_setting)
Settings.add_root_category(graphics_category)
func _register_audio_settings() -> void:
var audio_category = Settings.SettingCategory.new("audio", "Audio")
var master_volume_setting = Settings.FloatSetting.new("volume_master",
"Master Volume",
100.0,
0.0,
150.0,
1.0,
func(volume): _set_audio_bus_volume(volume, AudioBus.MASTER))
audio_category.add_setting(master_volume_setting)
# Fine Control subcategory for audio
var fine_control_category = Settings.SettingCategory.new("fine_control", "Fine Control")
var music_volume_setting = Settings.FloatSetting.new("volume_music",
"Volume (Music)",
100.0,
0.0,
150.0,
1.0,
func(volume): _set_audio_bus_volume(volume, AudioBus.MUSIC))
fine_control_category.add_setting(music_volume_setting)
var sfx_volume_setting = Settings.FloatSetting.new("volume_sfx",
"Volume (SFX)",
100.0,
0.0,
150.0,
1.0,
func(volume): _set_audio_bus_volume(volume, AudioBus.SFX))
fine_control_category.add_setting(sfx_volume_setting)
var voice_volume_setting = Settings.FloatSetting.new("volume_voice",
"Volume (Voice)",
100.0,
0.0,
150.0,
1.0,
func(volume): _set_audio_bus_volume(volume, AudioBus.VOICE))
fine_control_category.add_setting(voice_volume_setting)
audio_category.add_sub_category(fine_control_category)
Settings.add_root_category(audio_category)
func _register_controls_settings() -> void:
var controls_category = Settings.SettingCategory.new("controls", "Controls")
var mouse_sensitivity_setting = Settings.FloatSetting.new("mouse_sensitivity",
"Mouse Sensitivity",
1.0,
0.1,
1.9,
0.01)
controls_category.add_setting(mouse_sensitivity_setting)
var key_bindings_category = Settings.SettingCategory.new("key_bindings", "Key Bindings")
# Insert remappable actions
_create_action_setting(key_bindings_category, "move_up", "Move Forward")
_create_action_setting(key_bindings_category, "move_down", "Move Backward")
_create_action_setting(key_bindings_category, "move_left", "Move Left")
_create_action_setting(key_bindings_category, "move_right", "Move Right")
controls_category.add_sub_category(key_bindings_category)
Settings.add_root_category(controls_category)
#region Graphics Settings Callbacks
func _set_display_mode(display_mode_string: String):
var display_mode_index = ["Fullscreen", "Windowed Fullscreen", "Windowed"].find(display_mode_string)
match display_mode_index:
0: # FULLSCREEN
Global.game_manager.get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN
1: # WINDOWED_FULLSCREEN
Global.game_manager.get_window().mode = Window.MODE_FULLSCREEN
2: # WINDOWED
Global.game_manager.get_window().mode = Window.MODE_WINDOWED
func _set_vsync_enabled(enabled: bool):
var mode = DisplayServer.VSyncMode.VSYNC_ENABLED if enabled else DisplayServer.VSyncMode.VSYNC_DISABLED
DisplayServer.window_set_vsync_mode(mode) # Just for the main window
func _set_aa_mode_3d(mode_string: String):
var mode_index = ["Disabled", "FXAA", "TAA", "MSAA 2x", "MSAA 4x", "FSR 2"].find(mode_string)
var vp = Global.game_manager.get_viewport()
vp.use_taa = false
vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_DISABLED
vp.msaa_3d = Viewport.MSAA_DISABLED
vp.msaa_2d = Viewport.MSAA_DISABLED
vp.scaling_3d_mode = Viewport.SCALING_3D_MODE_BILINEAR
match mode_index:
1: vp.screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA
2: vp.use_taa = true # TAA
3: vp.msaa_3d = Viewport.MSAA_2X
4: vp.msaa_3d = Viewport.MSAA_4X
5: vp.scaling_3d_mode = Viewport.SCALING_3D_MODE_FSR2
func _set_aa_mode_2d(mode_string: String):
var mode_index = ["Disabled", "MSAA 2x", "MSAA 4x", "MSAA 8x"].find(mode_string)
var vp = Global.game_manager.get_viewport()
vp.msaa_2d = Viewport.MSAA_DISABLED
match mode_index:
1: vp.msaa_2d = Viewport.MSAA_2X # MSAA_2X
2: vp.msaa_2d = Viewport.MSAA_4X # MSAA_4X
3: vp.msaa_2d = Viewport.MSAA_8X # MSAA_8X
#endregion
#region Audio Settings Callbacks
func _set_audio_bus_volume(volume: float, bus: AudioBus):
volume /= 100.0
volume = volume*volume # Quadratic curve for better control on low end
match bus:
AudioBus.MASTER: AudioServer.set_bus_volume_linear(AudioServer.get_bus_index("Master"), volume)
AudioBus.MUSIC: AudioServer.set_bus_volume_linear(AudioServer.get_bus_index("Music"), volume)
AudioBus.SFX: AudioServer.set_bus_volume_linear(AudioServer.get_bus_index("SFX"), volume)
AudioBus.VOICE: AudioServer.set_bus_volume_linear(AudioServer.get_bus_index("Voice"), volume)
#endregion
#region Control Settings Callbacks
func _create_action_setting(category: Settings.SettingCategory, action: String, label: String) -> void:
var events = InputMap.action_get_events(action)
var default_events: Array[InputEvent] = []
for i in range(2):
if i < events.size():
default_events.append(events[i])
else:
default_events.append(null)
var setting = Settings.InputRemappingSetting.new("action_map_" + action,
label,
default_events,
func(new_events): _set_input_events(new_events, action))
category.add_setting(setting)
func _set_input_events(events: Array[InputEvent], action: String) -> void:
InputMap.action_erase_events(action)
for event in events:
if event:
InputMap.action_add_event(action, event)
#endregion

View File

@@ -0,0 +1 @@
uid://cijj8jj5gotdb

View File

@@ -0,0 +1,21 @@
[gd_resource type="AudioBusLayout" format=3 uid="uid://cxjrus2g7oh0t"]
[resource]
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"
bus/3/name = &"Voice"
bus/3/solo = false
bus/3/mute = false
bus/3/bypass_fx = false
bus/3/volume_db = 0.0
bus/3/send = &"Master"

View File

@@ -0,0 +1,184 @@
extends Node
class_name GameManager
## Use this when you have only one level
@export var main_level: PackedScene
## Whether the mouse should be captured while in a level
@export var is_mouse_captured_in_level: bool = true
@export_subgroup("Level Loading")
## Whether your game contains multiple levels.
## Uncheck if you only have one "Level" in your game
## If disabled, no level select option will be given in the title screen
@export var has_multiple_levels: bool = true
## The number of the highest level. For 1 Level this is 1
@export var max_level: int = 0
## Formatable Strig pointing to
@export var level_location = "res://levels/level_%s.tscn"
@onready var pause_menu: Control = %PauseMenu
@onready var menu_layer: CanvasLayer = %MenuLayer
var level = 0
var completed_levels: Array[bool] = []
var current_level_node: Node
func _ready() -> void:
Global.set_game_manager(self)
DebugGlobal.debug_label = %DebugLabel
for i in range(max_level):
completed_levels.append(false)
# Settings
var user_settings = UserDefinedSettings.new()
user_settings._register_settings()
Settings.load_config()
# Connect to InputManager
InputManager.game_pause.connect(pause)
InputManager.game_unpause.connect(resume)
InputManager.capture_mouse_ingame = is_mouse_captured_in_level
InputManager.set_is_in_game(false)
InputManager.set_is_paused(false)
_show_title_screen()
func _start_game() -> void:
level = 0
_show_controls()
#region Pausing
func pause():
InputManager.set_is_paused(true)
move_child(menu_layer, -1)
pause_menu.move_to_front()
pause_menu.show()
get_tree().paused = true
func resume():
InputManager.set_is_paused(false)
print("resume")
pause_menu.hide()
pause_menu.reset()
get_tree().paused = false
#endregion
#region Level Loading
func _unload_current_level() -> void:
if current_level_node != null:
current_level_node.queue_free()
current_level_node = null
func _show_main_level() -> void:
if main_level == null:
push_error("main_level is not set in GameManager")
return
InputManager.set_is_in_game(true)
var next_level: Node = main_level.instantiate()
if next_level.has_signal("win"):
next_level.win.connect(_next_level)
if next_level.has_signal("reset"):
next_level.reset.connect(_reload_current_level)
add_child(next_level)
current_level_node = next_level
func _next_level() -> void:
_unload_current_level()
if level < max_level and max_level > 0:
InputManager.set_is_in_game(true)
level += 1
_show_level(level)
completed_levels[level - 1] = true
else:
_show_win_screen()
func _show_level(level_nr: int) -> void:
# Clean up previous level if it exists (when coming from level select)
_unload_current_level()
InputManager.set_is_in_game(true)
level = level_nr
var next_level = load(level_location % str(level)).instantiate()
if next_level.has_signal("win"):
next_level.win.connect(_next_level)
if next_level.has_signal("reset"):
next_level.reset.connect(_reload_current_level)
add_child(next_level)
current_level_node = next_level
func _reload_current_level() -> void:
_unload_current_level()
if has_multiple_levels:
_show_level(level)
else:
_show_main_level()
#endregion
#region Showing Different GUI views
func _show_win_screen() -> void:
InputManager.set_is_in_game(false)
var win_screen: Control = load("res://ui/screens/win-screen/win_screen.tscn").instantiate()
win_screen.tree_exited.connect(_show_title_screen)
add_child(win_screen)
func _show_credits() -> void:
var credits: Node = load("res://ui/screens/credit-screen/credit_screen.tscn").instantiate()
credits.tree_exited.connect(_show_title_screen)
menu_layer.add_child(credits)
func _show_title_screen() -> void:
InputManager.set_is_in_game(false)
var title_screen: Node = load("res://ui/screens/title-screen/title_screen.tscn").instantiate()
title_screen.start_game.connect(_start_game)
title_screen.show_credits.connect(_show_credits)
title_screen.show_level_select.connect(_show_level_select)
title_screen.show_settings_screen.connect(_show_settings_screen)
title_screen.quit.connect(_quit_game)
title_screen.show_levels(has_multiple_levels)
menu_layer.add_child(title_screen)
func _show_level_select() -> void:
var level_select: Node = load("res://ui/screens/level-select-screen/level_select.tscn").instantiate()
level_select.start_level.connect(_show_level)
level_select.exit.connect(_show_title_screen)
level_select.init_buttons(max_level, completed_levels)
menu_layer.add_child(level_select)
func _show_settings_screen() -> void:
var settings_screen: Node = load("res://ui/screens/settings-screen/settings_screen.tscn").instantiate()
settings_screen.exit.connect(_show_title_screen)
menu_layer.add_child(settings_screen)
func _show_controls() -> void:
var controls: Node = load("res://ui/screens/control-screen/control_screen.tscn").instantiate()
if not main_level:
controls.tree_exited.connect(_next_level)
else:
controls.tree_exited.connect(_show_main_level)
menu_layer.add_child(controls)
func _return_to_title_screen() -> void:
get_tree().paused = false
InputManager.set_is_paused(false)
InputManager.set_is_in_game(false)
# Destroy level
if current_level_node != null:
current_level_node.queue_free()
current_level_node = null
_show_title_screen()
#endregion
func _quit_game() -> void:
get_tree().paused = false
get_tree().quit()
func set_world_environment(env: Environment):
$WorldEnvironment.environment = env

View File

@@ -0,0 +1 @@
uid://cluu0cgltsenj

View File

@@ -0,0 +1,36 @@
[gd_scene format=3 uid="uid://cgu0vbr50x7di"]
[ext_resource type="Script" uid="uid://cluu0cgltsenj" path="res://core/gamemanager.gd" id="1_htktm"]
[ext_resource type="PackedScene" uid="uid://bilai15byqef2" path="res://ui/screens/pause-menu/pause_menu.tscn" id="2_b2lg8"]
[ext_resource type="Environment" uid="uid://bhb4ckqipjvu3" path="res://core/main_environment.tres" id="2_v1n10"]
[ext_resource type="PackedScene" uid="uid://cyfvcuxi210mg" path="res://levels/main_level.tscn" id="2_xueuq"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="3_8qpx4"]
[node name="Gamemanager" type="Node" unique_id=1050225795]
process_mode = 3
script = ExtResource("1_htktm")
main_level = ExtResource("2_xueuq")
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=224925465]
environment = ExtResource("2_v1n10")
[node name="MenuLayer" type="CanvasLayer" parent="." unique_id=1393391734]
unique_name_in_owner = true
[node name="DebugLabel" type="Label" parent="MenuLayer" unique_id=2133776147]
unique_name_in_owner = true
visible = false
offset_left = 23.0
offset_top = 16.0
offset_right = 600.0
offset_bottom = 308.0
theme = ExtResource("3_8qpx4")
[node name="PauseMenu" parent="MenuLayer" unique_id=912678162 instance=ExtResource("2_b2lg8")]
unique_name_in_owner = true
visible = false
theme = ExtResource("3_8qpx4")
[connection signal="continue_btn_pressed" from="MenuLayer/PauseMenu" to="." method="resume"]
[connection signal="exit_game_btn_pressed" from="MenuLayer/PauseMenu" to="." method="_quit_game"]
[connection signal="exit_to_main_menu_btn_pressed" from="MenuLayer/PauseMenu" to="." method="_return_to_title_screen"]

View File

@@ -0,0 +1,15 @@
[gd_resource type="Environment" format=3 uid="uid://bhb4ckqipjvu3"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_e3rlx"]
sky_top_color = Color(0.218052, 0.202335, 0.225589, 1)
sky_horizon_color = Color(0.227003, 0.231896, 0.228184, 1)
ground_bottom_color = Color(0.167509, 0.160179, 0.160062, 1)
ground_horizon_color = Color(0.175353, 0.180521, 0.188678, 1)
[sub_resource type="Sky" id="Sky_n0kbt"]
sky_material = SubResource("ProceduralSkyMaterial_e3rlx")
[resource]
sky = SubResource("Sky_n0kbt")
tonemap_mode = 3
glow_enabled = true

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 994 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dkgp7tccxyyxc"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,6 @@
We recommend you put your levels here, if your game is level-based.
If you use this template's naming convention for levels, which is "level_<nr>.tscn",
where <nr> is replaced with a concrete number, the default settings of this template are able to pick up your level.
By default, level numbers should start with 1.
To use the templates default settings for progrssing through levels, please make your level emit a "win()"-signal.
To reinstanciate a level, make your level emit a "reset()"-signal

View File

@@ -0,0 +1,3 @@
[gd_scene format=3 uid="uid://cyfvcuxi210mg"]
[node name="Level1" type="Node" unique_id=407377819]

View File

@@ -0,0 +1,78 @@
; 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=5
[application]
config/name="Godot Game Template"
run/main_scene="res://core/gamemanager.tscn"
config/features=PackedStringArray("4.6", "GL Compatibility")
config/icon="res://icon.svg"
[audio]
buses/default_bus_layout="res://core/default_bus_layout.tres"
[autoload]
InputManager="*res://autoloads/input_manager.gd"
DebugGlobal="*res://autoloads/debug_global.gd"
Global="*res://autoloads/global.gd"
Settings="*res://autoloads/settings/settings.gd"
[display]
window/stretch/mode="canvas_items"
[file_customization]
folder_colors={
"res://autoloads/": "gray",
"res://core/": "red",
"res://levels/": "blue",
"res://ui/": "teal"
}
[input]
pause={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
toggle_debug_label={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
move_up={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
move_down={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
move_left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
move_right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
[rendering]
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 9H1V2l2 2a3.875 5 30 0 1 9 11 3.5 5 10 0 0-6-8z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b23e4kqj4o6dv"
path="res://.godot/imported/Revert.svg-83732f7446608123e95cc2f2a2cf35c6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/components/settings-menu/Revert.svg"
dest_files=["res://.godot/imported/Revert.svg-83732f7446608123e95cc2f2a2cf35c6.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
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,33 @@
extends Button
var event_idx = 0
signal action_changed(action: String)
func _ready():
set_process_unhandled_key_input(false)
func _toggled(b_pressed):
set_process_unhandled_key_input(b_pressed)
if b_pressed:
text = "..."
release_focus()
func _input(event):
if not button_pressed:
return
if event is InputEventKey or event is InputEventJoypadButton or event is InputEventMouseButton:
action_changed.emit(event)
get_viewport().set_input_as_handled()
button_pressed = false
func _display_event(event: InputEvent):
if event == null:
text = "Unassigned"
else:
text = event.as_text()
tooltip_text = text
func set_event(event: InputEvent):
_display_event(event)

View File

@@ -0,0 +1,9 @@
[gd_scene format=3 uid="uid://ngjm8kk8u7fm"]
[ext_resource type="Script" uid="uid://b7fb4ravo24q0" path="res://ui/components/settings-menu/action-remapping-button/action_remapping_button.gd" id="1_jb8u1"]
[node name="InputRemappingButton" type="Button" unique_id=740968636]
custom_minimum_size = Vector2(200, 0)
toggle_mode = true
text_overrun_behavior = 3
script = ExtResource("1_jb8u1")

View File

@@ -0,0 +1,184 @@
extends TabContainer
@onready var action_remapping_button_scene: PackedScene = load("res://ui/components/settings-menu/action-remapping-button/action_remapping_button.tscn")
func _ready() -> void:
call_deferred("_populate_menu")
func _populate_menu() -> void:
var tab_idx = 0
# Iterate through root categories (these become tabs)
for root_category in Settings.get_root_categories():
var margin_container = MarginContainer.new()
add_child(margin_container)
set_tab_title(tab_idx, root_category.display_text)
var scroll_container = ScrollContainer.new()
scroll_container.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
margin_container.add_child(scroll_container)
var margin_container2 = MarginContainer.new()
margin_container2.size_flags_horizontal = Control.SIZE_EXPAND_FILL
margin_container2.size_flags_vertical = Control.SIZE_EXPAND_FILL
scroll_container.add_child(margin_container2)
var cat_vbox = VBoxContainer.new()
cat_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
cat_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
margin_container2.add_child(cat_vbox)
# Add subcategories as sections, and their settings
_populate_category(cat_vbox, root_category, root_category.name)
tab_idx += 1
## Recursively populate a category and its subcategories
## Subcategories become section headers, deeper ones are flattened
func _populate_category(container: VBoxContainer, category: Settings.SettingCategory, path_prefix: String, depth: int = 0) -> void:
# If this is a subcategory (depth > 0), add a section header
if depth > 0:
var lbl_section_title = Label.new()
lbl_section_title.text = category.display_text
lbl_section_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lbl_section_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
lbl_section_title.theme_type_variation = "SettingsGroupLabel"
container.add_child(lbl_section_title)
container.add_child(HSeparator.new())
# Add all settings in this category
for setting in category.settings:
_add_setting_ui(container, setting, path_prefix + "/" + setting.name)
# Recursively add all subcategories
for sub_category in category.sub_categories:
var sub_path = path_prefix + "/" + sub_category.name
_populate_category(container, sub_category, sub_path, depth + 1)
func _add_setting_ui(container: VBoxContainer, setting: Settings.Setting, setting_path: String) -> void:
var hbox = HBoxContainer.new()
hbox.custom_minimum_size = Vector2(0, 40)
container.add_child(hbox)
# Another HBoxContainer for the label and revert button
var hbox_lbl = HBoxContainer.new()
hbox_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hbox.add_child(hbox_lbl)
# Label for the name of the setting
var lbl_name = Label.new()
lbl_name.text = setting.display_text
lbl_name.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hbox_lbl.add_child(lbl_name)
# Revert button for changing the setting back to its default value
# For input bindings a new button for each remapping is created later
if not setting is Settings.InputRemappingSetting:
var revert_btn = Button.new()
revert_btn.icon = get_theme_icon("icon", "RevertButton")
revert_btn.custom_minimum_size = Vector2(32, 32)
revert_btn.theme_type_variation = "FlatButton"
revert_btn.connect("pressed", func(): Settings.reset_value(setting_path))
hbox_lbl.add_child(revert_btn)
setting.value_changed.connect(func(__): _update_revert_button(setting_path, revert_btn))
_update_revert_button(setting_path, revert_btn)
# TODO: Tooltip
# Setting-specific editor
if setting is Settings.FloatSetting:
var slider_setting = setting as Settings.FloatSetting
var slider = HSlider.new()
slider.min_value = slider_setting.min_value
slider.max_value = slider_setting.max_value
slider.step = slider_setting.step
slider.value = slider_setting.value
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
slider.size_flags_vertical = Control.SIZE_FILL
slider.connect("value_changed", setting.set_value)
setting.value_changed.connect(slider.set_value_no_signal)
hbox.add_child(slider)
elif setting is Settings.OptionSetting:
var dropdown_setting = setting as Settings.OptionSetting
var option_button = OptionButton.new()
for i in range(dropdown_setting.options.size()):
option_button.add_item(dropdown_setting.options[i], i)
# Find the index of the current value
var current_idx = dropdown_setting.options.find(dropdown_setting.value)
if current_idx >= 0:
option_button.selected = current_idx
option_button.connect("item_selected", func(idx: int): setting.set_value(dropdown_setting.options[idx]))
setting.value_changed.connect(func(val): option_button.select(dropdown_setting.options.find(val)))
hbox.add_child(option_button)
elif setting is Settings.BoolSetting:
var checkbox = CheckBox.new()
checkbox.button_pressed = setting.value
checkbox.connect("toggled", setting.set_value)
setting.value_changed.connect(checkbox.set_pressed_no_signal)
hbox.add_child(checkbox)
elif setting is Settings.InputRemappingSetting:
_add_input_remapping_ui(hbox, setting as Settings.InputRemappingSetting, setting_path)
else:
# Assume it's a basic boolean setting if it has a bool value
if typeof(setting.value) == TYPE_BOOL:
var checkbox = CheckBox.new()
checkbox.button_pressed = setting.value
checkbox.connect("toggled", setting.set_value)
setting.value_changed.connect(checkbox.set_pressed_no_signal)
hbox.add_child(checkbox)
else:
push_warning("Unknown setting type for: " + setting.display_text)
func _add_input_remapping_ui(hbox: HBoxContainer, setting: Settings.InputRemappingSetting, setting_path: String) -> void:
var events = setting.value as Array[InputEvent]
# First reset button
var input_revert_btn_1 = Button.new()
input_revert_btn_1.icon = get_theme_icon("icon", "RevertButton")
input_revert_btn_1.custom_minimum_size = Vector2(32, 32)
input_revert_btn_1.theme_type_variation = "FlatButton"
input_revert_btn_1.connect("pressed", func(): Settings.reset_input_binding(setting_path, 0))
hbox.add_child(input_revert_btn_1)
setting.value_changed.connect(func(__): _update_input_mapping_revert_button(setting_path, 0, input_revert_btn_1))
_update_input_mapping_revert_button(setting_path, 0, input_revert_btn_1)
# First remapping button
var remapping_btn1 = action_remapping_button_scene.instantiate()
remapping_btn1.set_event(events[0] if events.size() > 0 else null)
remapping_btn1.connect("action_changed", func(event: InputEvent): _keymap_changed(event, 0, setting_path))
setting.value_changed.connect(func(value: Array[InputEvent]): _update_remapping_button(value, 0, remapping_btn1))
hbox.add_child(remapping_btn1)
# Second reset button
var input_revert_btn_2 = Button.new()
input_revert_btn_2.icon = get_theme_icon("icon", "RevertButton")
input_revert_btn_2.custom_minimum_size = Vector2(32, 32)
input_revert_btn_2.theme_type_variation = "FlatButton"
input_revert_btn_2.connect("pressed", func(): Settings.reset_input_binding(setting_path, 1))
hbox.add_child(input_revert_btn_2)
setting.value_changed.connect(func(__): _update_input_mapping_revert_button(setting_path, 1, input_revert_btn_2))
_update_input_mapping_revert_button(setting_path, 1, input_revert_btn_2)
# Second remapping button
var remapping_btn2 = action_remapping_button_scene.instantiate()
remapping_btn2.set_event(events[1] if events.size() > 1 else null)
remapping_btn2.connect("action_changed", func(event: InputEvent): _keymap_changed(event, 1, setting_path))
setting.value_changed.connect(func(value: Array[InputEvent]): _update_remapping_button(value, 1, remapping_btn2))
hbox.add_child(remapping_btn2)
func _keymap_changed(event: InputEvent, event_idx: int, setting_path: String) -> void:
var events = Settings.get_value(setting_path).duplicate()
if event_idx >= events.size():
events.push_back(null)
events[event_idx] = event
print("Keymap changed " + str(event_idx) + ":" + str(events))
Settings.set_value(setting_path, events)
func _update_revert_button(p_setting_path: String, button: Button) -> void:
button.visible = not Settings.is_value_default(p_setting_path)
func _update_remapping_button(value: Array[InputEvent], index: int, btn: Button):
btn.set_event(value[index] if value.size() > index else null)
func _update_input_mapping_revert_button(p_setting_path: String, index: int, button: Button) -> void:
button.modulate = Color.WHITE if not Settings.is_input_binding_default(p_setting_path, index) else Color.TRANSPARENT

View File

@@ -0,0 +1 @@
uid://ij8xcd6np4vt

View File

@@ -0,0 +1,12 @@
[gd_scene format=3 uid="uid://cv271fh4d2p13"]
[ext_resource type="Script" uid="uid://ij8xcd6np4vt" path="res://ui/components/settings-menu/settings_menu.gd" id="1_eae2p"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_scglv"]
[node name="SettingsMenu" type="TabContainer" unique_id=1538995254]
clip_contents = true
custom_minimum_size = Vector2(600, 400)
size_flags_vertical = 3
theme = ExtResource("1_scglv")
tab_alignment = 1
script = ExtResource("1_eae2p")

View File

@@ -0,0 +1,7 @@
extends Control
func _ready():
$Continue.grab_focus()
func _on_continue_pressed() -> void:
queue_free()

View File

@@ -0,0 +1 @@
uid://c1i00arsdf2mw

View File

@@ -0,0 +1,74 @@
[gd_scene format=3 uid="uid://bpr6du5ydm0lw"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_x8l5g"]
[ext_resource type="Script" uid="uid://c1i00arsdf2mw" path="res://ui/screens/control-screen/control_screen.gd" id="2_wdxwm"]
[sub_resource type="LabelSettings" id="LabelSettings_5tgjf"]
font_size = 60
[node name="ControlScreen" type="Control" unique_id=707664794]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_x8l5g")
script = ExtResource("2_wdxwm")
[node name="Panel" type="Panel" parent="." unique_id=1329424348]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="ControlsTitle" type="Label" parent="." unique_id=1553206497]
custom_minimum_size = Vector2(731.345, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -576.0
offset_top = 25.0
offset_right = 576.0
offset_bottom = 108.0
grow_horizontal = 2
text = "Controls"
label_settings = SubResource("LabelSettings_5tgjf")
horizontal_alignment = 1
autowrap_mode = 2
[node name="ControlsTitle2" type="Label" parent="." unique_id=394444381]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -86.5
offset_top = -11.5
offset_right = 86.5
offset_bottom = 11.5
grow_horizontal = 2
grow_vertical = 2
text = "Put your controls here"
horizontal_alignment = 1
[node name="Continue" type="Button" parent="." unique_id=426511693]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -39.5
offset_top = -58.0
offset_right = 39.5
offset_bottom = -27.0
grow_horizontal = 2
grow_vertical = 0
text = "Continue"
[connection signal="pressed" from="Continue" to="." method="_on_continue_pressed"]

View File

@@ -0,0 +1,20 @@
class_name CreditButton
extends VBoxContainer
signal display_license
var credit: CreditEntry
func set_credit(credit_entry: CreditEntry) -> void:
credit = credit_entry
if credit.descriptor:
$DescribtorLabel.text = credit.descriptor
$HBoxContainer/CreditButton.text = credit.name
if credit.url:
$HBoxContainer/CreditButton.pressed.connect(open_url)
func open_url():
OS.shell_open(credit.url)
func _on_license_button_pressed() -> void:
display_license.emit(credit)

View File

@@ -0,0 +1 @@
uid://6q5m84onxilu

View File

@@ -0,0 +1,39 @@
[gd_scene format=3 uid="uid://bhqw5lfvaq0bg"]
[ext_resource type="Script" uid="uid://6q5m84onxilu" path="res://ui/screens/credit-screen/credit_element.gd" id="1_ukcj5"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_2mwwd"]
[sub_resource type="LabelSettings" id="LabelSettings_p0wxk"]
font_size = 12
[node name="Credit" type="VBoxContainer" unique_id=69516036]
offset_right = 40.0
offset_bottom = 40.0
size_flags_horizontal = 3
theme_override_constants/separation = 0
alignment = 1
script = ExtResource("1_ukcj5")
[node name="HBoxContainer" type="HBoxContainer" parent="." unique_id=599791176]
layout_mode = 2
[node name="CreditButton" type="Button" parent="HBoxContainer" unique_id=955063757]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 20
theme_override_styles/focus = SubResource("StyleBoxEmpty_2mwwd")
flat = true
[node name="LicenseButton" type="Button" parent="HBoxContainer" unique_id=351433666]
layout_mode = 2
text = "License"
[node name="DescribtorLabel" type="Label" parent="." unique_id=322246210]
modulate = Color(1, 1, 1, 0.670588)
layout_mode = 2
label_settings = SubResource("LabelSettings_p0wxk")
horizontal_alignment = 1
autowrap_mode = 2
[connection signal="pressed" from="HBoxContainer/LicenseButton" to="." method="_on_license_button_pressed"]

View File

@@ -0,0 +1,8 @@
class_name CreditEntry
extends Resource
@export var name: String
@export var descriptor: String
@export var license: String
@export var license_file: String
@export var url: String

View File

@@ -0,0 +1 @@
uid://cjhxtjhj5o81f

View File

@@ -0,0 +1,84 @@
extends Control
@export var credits: String
func _ready() -> void:
$VBoxContainer/Return.grab_focus()
var credit_button_scene = load("res://ui/screens/credit-screen/credit_element.tscn")
var creds: Array[CreditEntry] = _dicts_to_entry(_read_credits())
for c in creds:
var b: CreditButton = credit_button_scene.instantiate()
b.display_license.connect(display_license)
b.set_credit(c)
$VBoxContainer/ScrollContainer/VBoxContainer/VBoxContainer.add_child(b)
$VBoxContainer/ScrollContainer.scroll_vertical = 0
func _on_return_pressed() -> void:
queue_free()
func _read_credits() -> Array:
var text = FileAccess.open(credits, FileAccess.READ).get_as_text()
var content_as_dictionary: Array = JSON.parse_string(text)
return content_as_dictionary
func _dicts_to_entry(array: Array) -> Array[CreditEntry]:
var item_array: Array[CreditEntry] = []
for dict in array:
var item = CreditEntry.new()
item.name = dict["name"]
item.descriptor = dict["descriptor"] if dict.has("descriptor") else ""
if dict.has("license"):
item.license = dict["license"] if dict["license"] else ""
elif dict.has("license_file"):
item.license = FileAccess.open(dict["license_file"], FileAccess.READ).get_as_text()\
if FileAccess.file_exists(dict["license_file"]) else ""
else:
item.license = ""
item.url = dict["url"] if dict.has("url") else ""
item_array.append(item)
return item_array
func _on_detailed_g_credits_pressed() -> void:
if not $VBoxContainer/ScrollContainer/Godot3PCredits.text:
var copyright_info = Engine.get_copyright_info()
var copyright_text = "Godot Engine Copyright Information:"
for entry in copyright_info:
copyright_text += "\n\n" + entry["name"] + ":\n"
var parts = entry["parts"]
for part in parts:
copyright_text += "Files:\n"
for file in part["files"]:
copyright_text += " " + file + "\n"
for copyright_owner in part["copyright"]:
copyright_text += "© " + copyright_owner + "\n"
copyright_text += "License:" + part["license"] + "\n"
# Append full license texts
var license_texts = Engine.get_license_info()
copyright_text += "\n\n\n\nFull license texts:"
for license in license_texts:
copyright_text += "\n\n" + license + ":\n"
copyright_text += license_texts[license]
$VBoxContainer/ScrollContainer/Godot3PCredits.text = copyright_text
$VBoxContainer/ScrollContainer/Godot3PCredits.visible = \
not $VBoxContainer/ScrollContainer/Godot3PCredits.visible
$VBoxContainer/ScrollContainer/VBoxContainer.visible = \
not $VBoxContainer/ScrollContainer/VBoxContainer.visible
if $VBoxContainer/ScrollContainer/Godot3PCredits.visible:
$DetailedGCredits.text = "Return to Credits"
else:
$DetailedGCredits.text = "Show detailed Godot Credits"
func display_license(credit: CreditEntry) -> void:
$LicenseText.visible = true
$LicenseText/VBoxContainer/Label.text = credit.name
$LicenseText/VBoxContainer/ScrollContainer/Label.text = credit.license
func _on_close_licence_text_pressed() -> void:
$LicenseText.visible = false

View File

@@ -0,0 +1 @@
uid://dx5iawpcgwhlm

View File

@@ -0,0 +1,116 @@
[gd_scene format=3 uid="uid://fd3xmnc047tm"]
[ext_resource type="Script" uid="uid://dx5iawpcgwhlm" path="res://ui/screens/credit-screen/credit_screen.gd" id="1_h6a2t"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="2_dvsi5"]
[sub_resource type="LabelSettings" id="LabelSettings_cutkl"]
font_size = 22
[sub_resource type="LabelSettings" id="LabelSettings_r8dx7"]
font_size = 12
[node name="CreditScreen" type="MarginContainer" unique_id=554750649]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("2_dvsi5")
script = ExtResource("1_h6a2t")
credits = "res://ui/screens/credit-screen/credits.json"
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1426215384]
layout_mode = 2
alignment = 1
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer" unique_id=193875904]
layout_mode = 2
size_flags_vertical = 3
horizontal_scroll_mode = 0
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer" unique_id=1766199227]
custom_minimum_size = Vector2(700, 0)
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 3
[node name="LabelCredits" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer" unique_id=500318037]
custom_minimum_size = Vector2(731.345, 0)
layout_mode = 2
theme_type_variation = &"HeaderLarge"
text = "Credits"
horizontal_alignment = 1
autowrap_mode = 2
[node name="Spacer2" type="Control" parent="VBoxContainer/ScrollContainer/VBoxContainer" unique_id=341639053]
custom_minimum_size = Vector2(0, 27.17)
layout_mode = 2
[node name="LabelCreatedBy" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer" unique_id=1161453390]
layout_mode = 2
text = "Created by You"
label_settings = SubResource("LabelSettings_cutkl")
horizontal_alignment = 1
[node name="LabelSpecialThanks" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer" unique_id=1866642574]
layout_mode = 2
text = "Special thanks to Also You"
horizontal_alignment = 1
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/ScrollContainer/VBoxContainer" unique_id=123317386]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer" unique_id=1405225546]
layout_mode = 2
[node name="Godot3PCredits" type="Label" parent="VBoxContainer/ScrollContainer" unique_id=1092186786]
visible = false
layout_mode = 2
size_flags_horizontal = 3
label_settings = SubResource("LabelSettings_r8dx7")
[node name="Return" type="Button" parent="VBoxContainer" unique_id=464459005]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
text = "Return to Main Menu
"
[node name="DetailedGCredits" type="Button" parent="." unique_id=729625426]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 8
theme_override_font_sizes/font_size = 12
text = "Show detailed Godot Credits"
flat = true
[node name="LicenseText" type="PanelContainer" parent="." unique_id=1702054193]
visible = false
layout_mode = 2
theme = ExtResource("2_dvsi5")
[node name="VBoxContainer" type="VBoxContainer" parent="LicenseText" unique_id=1286974170]
layout_mode = 2
[node name="Label" type="Label" parent="LicenseText/VBoxContainer" unique_id=426678051]
layout_mode = 2
size_flags_horizontal = 4
theme = ExtResource("2_dvsi5")
theme_type_variation = &"HeaderMedium"
[node name="ScrollContainer" type="ScrollContainer" parent="LicenseText/VBoxContainer" unique_id=1054289490]
layout_mode = 2
size_flags_vertical = 3
[node name="Label" type="Label" parent="LicenseText/VBoxContainer/ScrollContainer" unique_id=589755459]
layout_mode = 2
size_flags_horizontal = 6
[node name="CloseLicense" type="Button" parent="LicenseText/VBoxContainer" unique_id=102920052]
layout_mode = 2
size_flags_vertical = 4
text = "Close"
[connection signal="pressed" from="VBoxContainer/Return" to="." method="_on_return_pressed"]
[connection signal="pressed" from="DetailedGCredits" to="." method="_on_detailed_g_credits_pressed"]
[connection signal="pressed" from="LicenseText/VBoxContainer/CloseLicense" to="." method="_on_close_licence_text_pressed"]

View File

@@ -0,0 +1,18 @@
[
{
"name": "Godot",
"descriptor": "Open Source 2D/3D Game Engine",
"license": "This game uses Godot Engine, available under the following license:\n\nCopyright (c) 2014-present Godot Engine contributors.\nCopyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
"url": "https://godotengine.org/license"
},
{
"name": "Godot Game Template",
"descriptor": "Universal game template targeted at game jams.",
"license": "MIT License\n\nCopyright (c) 2025 Ekaterina Ehringhaus and Hendrik Brucker\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
"url": "https://github.com/Godot-Stammtisch-Karlsruhe/godot-game-template"
},
{
"name": "Sample Asset",
"descriptor": "Sample description (optional)"
}
]

View File

@@ -0,0 +1,11 @@
class_name LevelButton
extends Button
var level_nr: int
signal select_level(level_nr: int)
func _ready() -> void:
text = str(level_nr)
func _on_pressed() -> void:
select_level.emit(level_nr)

View File

@@ -0,0 +1 @@
uid://dwav7d4amw8yu

View File

@@ -0,0 +1,12 @@
[gd_scene format=3 uid="uid://ccrodv5x16nay"]
[ext_resource type="Script" uid="uid://dwav7d4amw8yu" path="res://ui/screens/level-select-screen/level_button.gd" id="1_ej8yx"]
[node name="LevelButton" type="Button" unique_id=923151870]
custom_minimum_size = Vector2(90, 90)
offset_right = 8.0
offset_bottom = 8.0
theme_override_font_sizes/font_size = 56
script = ExtResource("1_ej8yx")
[connection signal="pressed" from="." to="." method="_on_pressed"]

View File

@@ -0,0 +1,43 @@
extends Control
@export var buttonContainer: Container
var level_buttons: Array[Button] = []
var completed_levels: Array[bool] = []
signal start_level(level_nr: int)
signal test_level()
signal exit()
func init_buttons(max_level: int, completed_level_information: Array[bool]) -> void:
if max_level > 0:
$VBoxContainer/Label.visible = false
completed_levels = completed_level_information
for i in range(max_level):
var b: LevelButton = load("res://ui/screens/level-select-screen/level_button.tscn").instantiate()
b.level_nr = i + 1
b.select_level.connect(_level_button_pressed)
b.disabled = not completed_levels[i]
level_buttons.append(b)
buttonContainer.add_child(b)
func _level_button_pressed(level_nr: int) -> void:
print("Start Level ",level_nr)
start_level.emit(level_nr)
queue_free()
func _return_to_title() -> void:
exit.emit()
queue_free()
func _on_unlock_levels_toggled(toggled_on: bool) -> void:
for i in range(len(level_buttons)):
if toggled_on:
level_buttons[i].disabled = false
else:
level_buttons[i].disabled = !completed_levels[i]
func _on_test_level_pressed() -> void:
test_level.emit()
queue_free()

View File

@@ -0,0 +1 @@
uid://d2i8k7votngvg

View File

@@ -0,0 +1,66 @@
[gd_scene format=3 uid="uid://cv82k2twxie3n"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_cpj7q"]
[ext_resource type="Script" uid="uid://d2i8k7votngvg" path="res://ui/screens/level-select-screen/level_select.gd" id="2_lcog6"]
[sub_resource type="LabelSettings" id="LabelSettings_mfdjd"]
font_size = 60
[node name="LevelSelect" type="MarginContainer" unique_id=1286521924 node_paths=PackedStringArray("buttonContainer")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_cpj7q")
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
script = ExtResource("2_lcog6")
buttonContainer = NodePath("VBoxContainer/HFlowContainer")
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1716877818]
layout_mode = 2
[node name="LevelSelectTitle" type="Label" parent="VBoxContainer" unique_id=1290767086]
custom_minimum_size = Vector2(731.345, 0)
layout_mode = 2
text = "Level Select"
label_settings = SubResource("LabelSettings_mfdjd")
horizontal_alignment = 1
autowrap_mode = 2
[node name="Spacer" type="Control" parent="VBoxContainer" unique_id=1769227168]
custom_minimum_size = Vector2(0, 28)
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer" unique_id=878866277]
layout_mode = 2
text = "This Level Select Screen is provided by the template.
Numbered buttons related to a given level will be displayed here, if multiple_levels is enabled and the max number of levels is larger than zero.
The default location for levels ist the 'levels/' Folder with levels using the \"level_x.tscn\" naming scheme."
horizontal_alignment = 1
[node name="HFlowContainer" type="HFlowContainer" parent="VBoxContainer" unique_id=2027526310]
layout_mode = 2
alignment = 1
[node name="Control" type="HBoxContainer" parent="VBoxContainer" unique_id=1711230144]
layout_mode = 2
size_flags_vertical = 10
[node name="UnlockLevels" type="CheckButton" parent="VBoxContainer/Control" unique_id=1583371876]
layout_mode = 2
size_flags_horizontal = 10
text = "Unlock all Levels"
[node name="Return" type="Button" parent="VBoxContainer" unique_id=2120122741]
layout_mode = 2
size_flags_horizontal = 4
text = "Return to Main Menu"
[connection signal="toggled" from="VBoxContainer/Control/UnlockLevels" to="." method="_on_unlock_levels_toggled"]
[connection signal="pressed" from="VBoxContainer/Return" to="." method="_return_to_title"]

View File

@@ -0,0 +1,37 @@
extends Control
signal continue_btn_pressed
signal settings_btn_pressed
signal exit_to_main_menu_btn_pressed
signal exit_game_btn_pressed
func reset():
%SettingsMenuPopup.hide()
%MainPauseMenuPopup.show()
func _on_btn_continue_pressed():
continue_btn_pressed.emit()
hide()
func _on_btn_settings_pressed():
settings_btn_pressed.emit()
%SettingsMenuPopup.show()
%MainPauseMenuPopup.hide()
func _on_btn_exit_to_main_menu_pressed():
exit_to_main_menu_btn_pressed.emit()
hide()
func _on_btn_exit_game_pressed():
exit_game_btn_pressed.emit()
hide()
func _on_save_and_back_btn_pressed():
Settings.save_config()
reset()
func _on_continue_btn_visibility_changed() -> void:
if visible:
$CenterContainer/MainPauseMenuPopup/VBoxContainer/ContinueBtn.grab_focus()

View File

@@ -0,0 +1 @@
uid://2n7o5t6b5g4w

View File

@@ -0,0 +1,85 @@
[gd_scene format=3 uid="uid://bilai15byqef2"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_0kumn"]
[ext_resource type="Script" uid="uid://2n7o5t6b5g4w" path="res://ui/screens/pause-menu/pause_menu.gd" id="2_0mnak"]
[ext_resource type="PackedScene" uid="uid://cv271fh4d2p13" path="res://ui/components/settings-menu/settings_menu.tscn" id="4_e3p6x"]
[node name="PauseMenuScreen" type="Control" unique_id=2087442611]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2_0mnak")
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=983714819]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_0kumn")
[node name="MainPauseMenuPopup" type="PanelContainer" parent="CenterContainer" unique_id=1216636000]
unique_name_in_owner = true
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/MainPauseMenuPopup" unique_id=1361849718]
custom_minimum_size = Vector2(250, 0)
layout_mode = 2
[node name="PausedLbl" type="Label" parent="CenterContainer/MainPauseMenuPopup/VBoxContainer" unique_id=338515519]
layout_mode = 2
theme_type_variation = &"HeaderMedium"
text = "Paused"
horizontal_alignment = 1
[node name="ContinueBtn" type="Button" parent="CenterContainer/MainPauseMenuPopup/VBoxContainer" unique_id=2012541750]
layout_mode = 2
text = "Continue"
[node name="SettingsButton" type="Button" parent="CenterContainer/MainPauseMenuPopup/VBoxContainer" unique_id=1680382734]
layout_mode = 2
text = "Settings"
[node name="ExitToMainMenuBtn" type="Button" parent="CenterContainer/MainPauseMenuPopup/VBoxContainer" unique_id=300221649]
layout_mode = 2
text = "Quit to title screen"
[node name="ExitGameBtn" type="Button" parent="CenterContainer/MainPauseMenuPopup/VBoxContainer" unique_id=104298902]
layout_mode = 2
text = "Quit game"
[node name="SettingsMenuPopup" type="PanelContainer" parent="CenterContainer" unique_id=1654694978]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme = ExtResource("1_0kumn")
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/SettingsMenuPopup" unique_id=680704985]
layout_mode = 2
theme_override_constants/separation = 12
[node name="Label" type="Label" parent="CenterContainer/SettingsMenuPopup/VBoxContainer" unique_id=547993980]
layout_mode = 2
theme_type_variation = &"HeaderMedium"
text = "Settings"
horizontal_alignment = 1
[node name="SettingsMenu" parent="CenterContainer/SettingsMenuPopup/VBoxContainer" unique_id=1510978317 instance=ExtResource("4_e3p6x")]
layout_mode = 2
[node name="SaveAndBackBtn" type="Button" parent="CenterContainer/SettingsMenuPopup/VBoxContainer" unique_id=312254833]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
text = "Save and back"
[connection signal="pressed" from="CenterContainer/MainPauseMenuPopup/VBoxContainer/ContinueBtn" to="." method="_on_btn_continue_pressed"]
[connection signal="visibility_changed" from="CenterContainer/MainPauseMenuPopup/VBoxContainer/ContinueBtn" to="." method="_on_continue_btn_visibility_changed"]
[connection signal="pressed" from="CenterContainer/MainPauseMenuPopup/VBoxContainer/SettingsButton" to="." method="_on_btn_settings_pressed"]
[connection signal="pressed" from="CenterContainer/MainPauseMenuPopup/VBoxContainer/ExitToMainMenuBtn" to="." method="_on_btn_exit_to_main_menu_pressed"]
[connection signal="pressed" from="CenterContainer/MainPauseMenuPopup/VBoxContainer/ExitGameBtn" to="." method="_on_btn_exit_game_pressed"]
[connection signal="pressed" from="CenterContainer/SettingsMenuPopup/VBoxContainer/SaveAndBackBtn" to="." method="_on_save_and_back_btn_pressed"]

View File

@@ -0,0 +1,11 @@
extends MarginContainer
signal exit()
func _ready():
$VBoxContainer/Return.grab_focus()
func _return_to_title_screen() -> void:
exit.emit()
queue_free()
Settings.save_config()

View File

@@ -0,0 +1 @@
uid://roiysiotwsbq

View File

@@ -0,0 +1,46 @@
[gd_scene format=3 uid="uid://ywybicp1ess8"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_i4lq1"]
[ext_resource type="Script" uid="uid://roiysiotwsbq" path="res://ui/screens/settings-screen/settings_screen.gd" id="2_ck7g6"]
[ext_resource type="PackedScene" uid="uid://cv271fh4d2p13" path="res://ui/components/settings-menu/settings_menu.tscn" id="4_i4lq1"]
[node name="OptionsMenu" type="MarginContainer" unique_id=420614935]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 0
theme = ExtResource("1_i4lq1")
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
script = ExtResource("2_ck7g6")
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1967046289]
custom_minimum_size = Vector2(450, 0)
layout_mode = 2
size_flags_horizontal = 4
theme_override_constants/separation = 10
[node name="OptionsTitle" type="Label" parent="VBoxContainer" unique_id=1806842321]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
size_flags_vertical = 0
theme_type_variation = &"HeaderLarge"
text = "Options"
horizontal_alignment = 1
autowrap_mode = 2
[node name="SettingsMenu" parent="VBoxContainer" unique_id=784596614 instance=ExtResource("4_i4lq1")]
custom_minimum_size = Vector2(800, 400)
layout_mode = 2
[node name="Return" type="Button" parent="VBoxContainer" unique_id=1869177324]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
text = "Return to Main Menu"
[connection signal="pressed" from="VBoxContainer/Return" to="." method="_return_to_title_screen"]

View File

@@ -0,0 +1,33 @@
extends Control
signal start_game()
signal show_credits()
signal show_level_select()
signal show_settings_screen()
signal quit()
func _ready():
$CenterContainer2/VBoxContainer/Start.grab_focus()
func _on_start_pressed() -> void:
start_game.emit()
queue_free()
func _on_credit_pressed() -> void:
show_credits.emit()
queue_free()
func _on_level_select_pressed() -> void:
show_level_select.emit()
queue_free()
func _on_options_pressed() -> void:
show_settings_screen.emit()
queue_free()
func _on_quit_pressed():
quit.emit()
queue_free()
func show_levels(b: bool) -> void:
$CenterContainer2/VBoxContainer/LevelSelect.visible = b

View File

@@ -0,0 +1 @@
uid://1mjt83fygiua

View File

@@ -0,0 +1,116 @@
[gd_scene format=3 uid="uid://ddc3qfst7mbgc"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_qt2rq"]
[ext_resource type="Script" uid="uid://1mjt83fygiua" path="res://ui/screens/title-screen/title_screen.gd" id="1_rm86k"]
[sub_resource type="LabelSettings" id="LabelSettings_s0ue6"]
font_size = 150
[sub_resource type="LabelSettings" id="LabelSettings_73xnf"]
font_size = 50
[sub_resource type="LabelSettings" id="LabelSettings_wly7t"]
font_size = 12
[node name="TitleScreen" type="Control" unique_id=165552926]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_qt2rq")
script = ExtResource("1_rm86k")
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=129658239]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 2.0
offset_top = 20.0
offset_right = 2.0
offset_bottom = -269.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer" unique_id=1543886662]
layout_mode = 2
[node name="Label" type="Label" parent="CenterContainer/VBoxContainer" unique_id=457043767]
custom_minimum_size = Vector2(731.345, 0)
layout_mode = 2
text = "R.A.M."
label_settings = SubResource("LabelSettings_s0ue6")
horizontal_alignment = 1
autowrap_mode = 2
[node name="Label2" type="Label" parent="CenterContainer/VBoxContainer" unique_id=2117014839]
custom_minimum_size = Vector2(731.345, 0)
layout_mode = 2
text = "rouge-like about machines"
label_settings = SubResource("LabelSettings_73xnf")
horizontal_alignment = 1
autowrap_mode = 2
[node name="CenterContainer2" type="CenterContainer" parent="." unique_id=1706430698]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = 309.0
offset_bottom = -28.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer2" unique_id=11688683]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
[node name="Start" type="Button" parent="CenterContainer2/VBoxContainer" unique_id=603626461]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Start"
[node name="LevelSelect" type="Button" parent="CenterContainer2/VBoxContainer" unique_id=1964255967]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Level Select"
[node name="Options" type="Button" parent="CenterContainer2/VBoxContainer" unique_id=1650158032]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Settings"
[node name="Credit" type="Button" parent="CenterContainer2/VBoxContainer" unique_id=426848008]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Credits"
[node name="Quit" type="Button" parent="CenterContainer2/VBoxContainer" unique_id=2004677633]
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Quit"
[node name="Label" type="Label" parent="." unique_id=1786336606]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -76.5
offset_top = -25.0
offset_right = 76.5
offset_bottom = -2.0
grow_horizontal = 2
grow_vertical = 0
text = "My Version"
label_settings = SubResource("LabelSettings_wly7t")
horizontal_alignment = 1
[connection signal="pressed" from="CenterContainer2/VBoxContainer/Start" to="." method="_on_start_pressed"]
[connection signal="pressed" from="CenterContainer2/VBoxContainer/LevelSelect" to="." method="_on_level_select_pressed"]
[connection signal="pressed" from="CenterContainer2/VBoxContainer/Options" to="." method="_on_options_pressed"]
[connection signal="pressed" from="CenterContainer2/VBoxContainer/Credit" to="." method="_on_credit_pressed"]
[connection signal="pressed" from="CenterContainer2/VBoxContainer/Quit" to="." method="_on_quit_pressed"]

View File

@@ -0,0 +1,7 @@
extends Control
func _ready():
$CenterContainer/VBoxContainer/Button.grab_focus()
func _on_button_pressed() -> void:
queue_free()

View File

@@ -0,0 +1 @@
uid://dgtme0a6lym5c

View File

@@ -0,0 +1,51 @@
[gd_scene format=3 uid="uid://g2g8hydsgxid"]
[ext_resource type="Script" uid="uid://dgtme0a6lym5c" path="res://ui/screens/win-screen/win_screen.gd" id="1_lgw0b"]
[sub_resource type="LabelSettings" id="LabelSettings_rbmgw"]
font_size = 100
[sub_resource type="LabelSettings" id="LabelSettings_rvyxm"]
font_size = 25
[node name="WinScreen" type="Control" unique_id=858939810]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_lgw0b")
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=2007437466]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer" unique_id=1911103298]
layout_mode = 2
[node name="Label" type="Label" parent="CenterContainer/VBoxContainer" unique_id=1273209801]
custom_minimum_size = Vector2(731.345, 0)
layout_mode = 2
size_flags_vertical = 3
text = "You won!"
label_settings = SubResource("LabelSettings_rbmgw")
horizontal_alignment = 1
autowrap_mode = 2
[node name="Label2" type="Label" parent="CenterContainer/VBoxContainer" unique_id=1313320644]
layout_mode = 2
text = "Congratulations and thank you for playing"
label_settings = SubResource("LabelSettings_rvyxm")
horizontal_alignment = 1
[node name="Button" type="Button" parent="CenterContainer/VBoxContainer" unique_id=533980693]
layout_mode = 2
size_flags_horizontal = 4
text = "Return to title screen"
[connection signal="pressed" from="CenterContainer/VBoxContainer/Button" to="." method="_on_button_pressed"]

View File

@@ -0,0 +1,24 @@
[gd_resource type="Theme" format=3 uid="uid://hheneshfv1b2"]
[ext_resource type="Texture2D" uid="uid://b23e4kqj4o6dv" path="res://ui/components/settings-menu/Revert.svg" id="1_r1020"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0d4dl"]
content_margin_left = 8.0
content_margin_top = 8.0
content_margin_right = 8.0
content_margin_bottom = 8.0
bg_color = Color(0.212305, 0.212305, 0.212305, 1)
corner_radius_top_left = 2
corner_radius_top_right = 2
corner_radius_bottom_right = 2
corner_radius_bottom_left = 2
[resource]
HeaderLarge/font_sizes/font_size = 56
HeaderMedium/font_sizes/font_size = 24
MarginContainer/constants/margin_bottom = 8
MarginContainer/constants/margin_left = 8
MarginContainer/constants/margin_right = 8
MarginContainer/constants/margin_top = 8
PanelContainer/styles/panel = SubResource("StyleBoxFlat_0d4dl")
RevertButton/icons/icon = ExtResource("1_r1020")