diff --git a/.gitignore b/.gitignore
index e257658..867d4b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,15 +11,13 @@
# Precompiled Headers
*.gch
*.pch
+*.pdb
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
-
-# Fortran module files
-*.mod
-*.smod
+*.exp
# Compiled Static libraries
*.lai
@@ -31,4 +29,9 @@
*.exe
*.out
*.app
+*.dll
+
+# ---> Platform Files
+*.sh
+*.*.dblite
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..8441cec
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "src/godot-cpp"]
+ path = src/godot-cpp
+ url = https://github.com/godotengine/godot-cpp
+ branch = 4.2
diff --git a/Classes/LoadingScreen/loading_screen.gd b/Classes/LoadingScreen/loading_screen.gd
new file mode 100644
index 0000000..77a8c8e
--- /dev/null
+++ b/Classes/LoadingScreen/loading_screen.gd
@@ -0,0 +1,32 @@
+extends CanvasLayer
+
+@onready var animation_player : AnimationPlayer = $AnimationPlayer
+
+signal load_ready
+signal beginning_fade_out
+signal load_finished
+
+
+func begin_fade_in():
+ animation_player.animation_finished.connect(_load_screen_ready)
+ animation_player.play("fade_in")
+
+func _load_screen_ready(_anim_name:StringName):
+ animation_player.animation_finished.disconnect(_load_screen_ready)
+ emit_signal("load_ready")
+
+
+func begin_fade_out():
+ animation_player.animation_finished.connect(_continue_fade_out)
+ animation_player.play("fade_out_1")
+
+func _continue_fade_out(_anim_name:StringName):
+ animation_player.animation_finished.disconnect(_continue_fade_out)
+ emit_signal("beginning_fade_out")
+
+ animation_player.animation_finished.connect(_load_screen_disabled)
+ animation_player.play("fade_out_2")
+
+func _load_screen_disabled(_anim_name:StringName):
+ animation_player.animation_finished.disconnect(_load_screen_disabled)
+ emit_signal("load_finished")
diff --git a/Classes/LoadingScreen/loading_screen.gd.uid b/Classes/LoadingScreen/loading_screen.gd.uid
new file mode 100644
index 0000000..e2e570a
--- /dev/null
+++ b/Classes/LoadingScreen/loading_screen.gd.uid
@@ -0,0 +1 @@
+uid://b3bafn47rxsbi
diff --git a/Classes/LoadingScreen/loading_screen.tscn b/Classes/LoadingScreen/loading_screen.tscn
new file mode 100644
index 0000000..57150bd
--- /dev/null
+++ b/Classes/LoadingScreen/loading_screen.tscn
@@ -0,0 +1,155 @@
+[gd_scene load_steps=9 format=3 uid="uid://d0d7k8ebwixgp"]
+
+[ext_resource type="Script" path="res://addons/liborng/Classes/LoadingScreen/loading_screen.gd" id="1_chnhr"]
+[ext_resource type="PackedScene" uid="uid://ccy6uvk0tjnsn" path="res://UI/Widgets/LoadingThrobber/circular_throbber.tscn" id="2_at3lf"]
+
+[sub_resource type="Animation" id="Animation_sbna0"]
+length = 0.001
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("ColorRect:modulate:a")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+tracks/1/type = "bezier"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("ColorRect/Margin:modulate:a")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+
+[sub_resource type="Animation" id="Animation_xu6eh"]
+resource_name = "fade_in"
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("ColorRect:modulate:a")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0, 0),
+"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0, 1)
+}
+tracks/1/type = "bezier"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("ColorRect/Margin:modulate:a")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"handle_modes": PackedInt32Array(0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0)
+}
+
+[sub_resource type="Animation" id="Animation_6awqr"]
+resource_name = "fade_out_1"
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("ColorRect/Margin:modulate:a")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0, 0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0, 1)
+}
+
+[sub_resource type="Animation" id="Animation_0ve6d"]
+resource_name = "fade_out_2"
+tracks/0/type = "bezier"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("ColorRect:modulate:a")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"handle_modes": PackedInt32Array(0, 0),
+"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
+"times": PackedFloat32Array(0, 1)
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_c0l58"]
+_data = {
+"RESET": SubResource("Animation_sbna0"),
+"fade_in": SubResource("Animation_xu6eh"),
+"fade_out_1": SubResource("Animation_6awqr"),
+"fade_out_2": SubResource("Animation_0ve6d")
+}
+
+[sub_resource type="LabelSettings" id="LabelSettings_tce6x"]
+font_size = 40
+
+[node name="LoadingScreen" type="CanvasLayer"]
+layer = 64
+script = ExtResource("1_chnhr")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+libraries = {
+"": SubResource("AnimationLibrary_c0l58")
+}
+
+[node name="ColorRect" type="ColorRect" parent="."]
+modulate = Color(1, 1, 1, 0)
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+color = Color(0, 0, 0, 1)
+
+[node name="Margin" type="Control" parent="ColorRect"]
+modulate = Color(1, 1, 1, 0)
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = 80.0
+offset_top = 80.0
+offset_right = -80.0
+offset_bottom = -80.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="VBoxContainer" type="VBoxContainer" parent="ColorRect/Margin"]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="Spacer" type="Control" parent="ColorRect/Margin/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="HBoxContainer" type="HBoxContainer" parent="ColorRect/Margin/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="Control" type="Control" parent="ColorRect/Margin/VBoxContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="LoadingLabel" type="Label" parent="ColorRect/Margin/VBoxContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 1
+text = "Loading..."
+label_settings = SubResource("LabelSettings_tce6x")
+vertical_alignment = 2
+uppercase = true
+
+[node name="CircularThrobber" parent="ColorRect/Margin/VBoxContainer/HBoxContainer" instance=ExtResource("2_at3lf")]
+layout_mode = 2
diff --git a/Classes/PlayerSpawns/player_origin.tscn b/Classes/PlayerSpawns/player_origin.tscn
new file mode 100644
index 0000000..abfb251
--- /dev/null
+++ b/Classes/PlayerSpawns/player_origin.tscn
@@ -0,0 +1,10 @@
+[gd_scene load_steps=2 format=3 uid="uid://cp7nwg5k1304b"]
+
+[ext_resource type="ArrayMesh" uid="uid://ck4rwqbhr2sgd" path="res://Assets/Models/gobot/model/gobot.res" id="1_gie0i"]
+
+[node name="PlayerOrigin" type="PlayerOrigin"]
+
+[node name="CharacterMarker" type="MeshInstance3D" parent="."]
+layers = 524288
+mesh = ExtResource("1_gie0i")
+skeleton = NodePath("")
diff --git a/Classes/PlayerSpawns/player_spawn_autosave.tscn b/Classes/PlayerSpawns/player_spawn_autosave.tscn
new file mode 100644
index 0000000..9368d86
--- /dev/null
+++ b/Classes/PlayerSpawns/player_spawn_autosave.tscn
@@ -0,0 +1,23 @@
+[gd_scene load_steps=3 format=3 uid="uid://b8undgwd6nkr3"]
+
+[ext_resource type="ArrayMesh" uid="uid://ck4rwqbhr2sgd" path="res://Assets/Models/gobot/model/gobot.res" id="1_oa04u"]
+
+[sub_resource type="BoxShape3D" id="BoxShape3D_r2ywv"]
+
+[node name="PlayerSpawn" type="PlayerSpawn"]
+
+[node name="CharacterMarker" type="MeshInstance3D" parent="."]
+layers = 524288
+mesh = ExtResource("1_oa04u")
+skeleton = NodePath("")
+
+[node name="Area3D" type="Area3D" parent="."]
+collision_mask = 8
+
+[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
+shape = SubResource("BoxShape3D_r2ywv")
+debug_color = Color(0.933333, 0.533333, 0, 1)
+debug_fill = true
+
+[connection signal="body_entered" from="Area3D" to="." method="_on_area_3d_body_entered" flags=7]
diff --git a/icons/icon_hide_in_game.svg b/icons/icon_hide_in_game.svg
new file mode 100644
index 0000000..3c37d84
--- /dev/null
+++ b/icons/icon_hide_in_game.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/icons/icon_hide_in_game.svg.import b/icons/icon_hide_in_game.svg.import
new file mode 100644
index 0000000..5302257
--- /dev/null
+++ b/icons/icon_hide_in_game.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d4ju12k38cix8"
+path="res://.godot/imported/icon_hide_in_game.svg-00f4731bef975231519c8b345a64eeeb.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/liborng/icons/icon_hide_in_game.svg"
+dest_files=["res://.godot/imported/icon_hide_in_game.svg-00f4731bef975231519c8b345a64eeeb.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
diff --git a/icons/icon_level_scene.svg b/icons/icon_level_scene.svg
new file mode 100644
index 0000000..c415d3b
--- /dev/null
+++ b/icons/icon_level_scene.svg
@@ -0,0 +1,49 @@
+
+
diff --git a/icons/icon_level_scene.svg.import b/icons/icon_level_scene.svg.import
new file mode 100644
index 0000000..6de7992
--- /dev/null
+++ b/icons/icon_level_scene.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cs1wpijdxyuil"
+path="res://.godot/imported/icon_level_scene.svg-637d7ee8c3ab5cca2fe606d4a457d445.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/liborng/icons/icon_level_scene.svg"
+dest_files=["res://.godot/imported/icon_level_scene.svg-637d7ee8c3ab5cca2fe606d4a457d445.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
diff --git a/icons/icon_player_origin.svg b/icons/icon_player_origin.svg
new file mode 100644
index 0000000..80f60cc
--- /dev/null
+++ b/icons/icon_player_origin.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/icons/icon_player_origin.svg.import b/icons/icon_player_origin.svg.import
new file mode 100644
index 0000000..e0b80f2
--- /dev/null
+++ b/icons/icon_player_origin.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://gqs2memdbyhe"
+path="res://.godot/imported/icon_player_origin.svg-6f9f6b601de03389af3c9c0297f36d4d.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/liborng/icons/icon_player_origin.svg"
+dest_files=["res://.godot/imported/icon_player_origin.svg-6f9f6b601de03389af3c9c0297f36d4d.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
diff --git a/icons/icon_player_spawn.svg b/icons/icon_player_spawn.svg
new file mode 100644
index 0000000..ee79cda
--- /dev/null
+++ b/icons/icon_player_spawn.svg
@@ -0,0 +1,49 @@
+
+
diff --git a/icons/icon_player_spawn.svg.import b/icons/icon_player_spawn.svg.import
new file mode 100644
index 0000000..daa3bcc
--- /dev/null
+++ b/icons/icon_player_spawn.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dttfpr6rgp27x"
+path="res://.godot/imported/icon_player_spawn.svg-68e09c0699bcfa6d35101c195826e90f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/liborng/icons/icon_player_spawn.svg"
+dest_files=["res://.godot/imported/icon_player_spawn.svg-68e09c0699bcfa6d35101c195826e90f.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
diff --git a/icons/icon_sublevel_loader.svg b/icons/icon_sublevel_loader.svg
new file mode 100644
index 0000000..c99d4e8
--- /dev/null
+++ b/icons/icon_sublevel_loader.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/icons/icon_sublevel_loader.svg.import b/icons/icon_sublevel_loader.svg.import
new file mode 100644
index 0000000..2fe7a10
--- /dev/null
+++ b/icons/icon_sublevel_loader.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cikjmm4ub7svl"
+path="res://.godot/imported/icon_sublevel_loader.svg-941799faa2b7141fb320dc9df09d71ed.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/liborng/icons/icon_sublevel_loader.svg"
+dest_files=["res://.godot/imported/icon_sublevel_loader.svg-941799faa2b7141fb320dc9df09d71ed.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
diff --git a/icons/icon_sublevel_scene.svg b/icons/icon_sublevel_scene.svg
new file mode 100644
index 0000000..4dbd658
--- /dev/null
+++ b/icons/icon_sublevel_scene.svg
@@ -0,0 +1,49 @@
+
+
diff --git a/icons/icon_sublevel_scene.svg.import b/icons/icon_sublevel_scene.svg.import
new file mode 100644
index 0000000..c377d9e
--- /dev/null
+++ b/icons/icon_sublevel_scene.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dlkm4nlsxrgjh"
+path="res://.godot/imported/icon_sublevel_scene.svg-151f3f284d23ec1ac347327e5d33c20b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/liborng/icons/icon_sublevel_scene.svg"
+dest_files=["res://.godot/imported/icon_sublevel_scene.svg-151f3f284d23ec1ac347327e5d33c20b.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
diff --git a/models/arrow.blend b/models/arrow.blend
new file mode 100644
index 0000000..85ad2f5
Binary files /dev/null and b/models/arrow.blend differ
diff --git a/models/arrow.blend.import b/models/arrow.blend.import
new file mode 100644
index 0000000..98e51e5
--- /dev/null
+++ b/models/arrow.blend.import
@@ -0,0 +1,63 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://ccmxkk7gt2vqk"
+path="res://.godot/imported/arrow.blend-6c3bbbee6c78384920b0d823e32e43b5.scn"
+
+[deps]
+
+source_file="res://addons/liborng/models/arrow.blend"
+dest_files=["res://.godot/imported/arrow.blend-6c3bbbee6c78384920b0d823e32e43b5.scn"]
+
+[params]
+
+nodes/root_type=""
+nodes/root_name=""
+nodes/root_script=null
+mesh_library/use_node_names_as_mesh_names=false
+array_mesh/deduplicate_surfaces=true
+nodes/apply_root_scale=true
+nodes/root_scale=1.0
+nodes/import_as_skeleton_bones=false
+nodes/use_name_suffixes=true
+nodes/use_node_type_suffixes=true
+meshes/ensure_tangents=true
+meshes/generate_lods=true
+meshes/create_shadow_meshes=true
+meshes/light_baking=1
+meshes/lightmap_texel_size=0.2
+meshes/force_disable_compression=false
+skins/use_named_skins=true
+animation/import=true
+animation/fps=30
+animation/trimming=false
+animation/remove_immutable_tracks=true
+animation/import_rest_as_RESET=false
+import_script/path=""
+materials/extract=0
+materials/extract_format=0
+materials/extract_path=""
+_subresources={}
+blender/nodes/visible=0
+blender/nodes/active_collection_only=false
+blender/nodes/punctual_lights=true
+blender/nodes/cameras=true
+blender/nodes/custom_properties=true
+blender/nodes/modifiers=1
+blender/meshes/vertex_colors=1
+blender/meshes/uvs=true
+blender/meshes/normals=true
+blender/meshes/export_geometry_nodes_instances=false
+blender/meshes/gpu_instances=false
+blender/meshes/tangents=true
+blender/meshes/skins=2
+blender/meshes/export_bones_deforming_mesh_only=false
+blender/materials/unpack_enabled=true
+blender/materials/export_materials=1
+blender/animation/limit_playback=true
+blender/animation/always_sample=true
+blender/animation/group_tracks=true
+gltf/naming_version=2
+gltf/texture_map_mode=1
diff --git a/models/arrow.blend1 b/models/arrow.blend1
new file mode 100644
index 0000000..c7267bd
Binary files /dev/null and b/models/arrow.blend1 differ
diff --git a/models/arrow.material b/models/arrow.material
new file mode 100644
index 0000000..f4587c0
Binary files /dev/null and b/models/arrow.material differ
diff --git a/models/arrow.mtl b/models/arrow.mtl
new file mode 100644
index 0000000..02fe8db
--- /dev/null
+++ b/models/arrow.mtl
@@ -0,0 +1,2 @@
+# Blender 3.3.6 MTL File: 'arrow.blend'
+# www.blender.org
diff --git a/models/arrow.obj.import b/models/arrow.obj.import
new file mode 100644
index 0000000..2e43e6f
--- /dev/null
+++ b/models/arrow.obj.import
@@ -0,0 +1,25 @@
+[remap]
+
+importer="wavefront_obj"
+importer_version=1
+type="Mesh"
+uid="uid://qp22dk8rq66i"
+path="res://.godot/imported/arrow.obj-53a59cb0d82f2c2071a9e76b12c0a99d.mesh"
+
+[deps]
+
+files=["res://.godot/imported/arrow.obj-53a59cb0d82f2c2071a9e76b12c0a99d.mesh"]
+
+source_file="res://addons/liborng/models/arrow.obj"
+dest_files=["res://.godot/imported/arrow.obj-53a59cb0d82f2c2071a9e76b12c0a99d.mesh", "res://.godot/imported/arrow.obj-53a59cb0d82f2c2071a9e76b12c0a99d.mesh"]
+
+[params]
+
+generate_tangents=true
+generate_lods=true
+generate_shadow_mesh=true
+generate_lightmap_uv2=false
+generate_lightmap_uv2_texel_size=0.2
+scale_mesh=Vector3(1, 1, 1)
+offset_mesh=Vector3(0, 0, 0)
+force_disable_mesh_compression=false
diff --git a/models/spawn_marker.res b/models/spawn_marker.res
new file mode 100644
index 0000000..758db87
Binary files /dev/null and b/models/spawn_marker.res differ
diff --git a/orng.gdextension b/orng.gdextension
new file mode 100644
index 0000000..09a3e7a
--- /dev/null
+++ b/orng.gdextension
@@ -0,0 +1,20 @@
+[configuration]
+
+entry_symbol = "orng_library_init"
+compatibility_minimum = "4.2"
+reloadable = true
+
+[libraries]
+
+windows.debug.x86_64 = "windows_x86_64/template_debug/liborng.dll"
+windows.release.x86_64 = "windows_x86_64/template_release/liborng.dll"
+web.debug.wasm32 = "web_wasm32/template_debug/liborng.wasm"
+web.release.wasm32 = "web_wasm32/template_release/liborng.wasm"
+
+[icons]
+HideInGame = "res://addons/liborng/icons/icon_hide_in_game.svg"
+LevelScene = "res://addons/liborng/icons/icon_level_scene.svg"
+PlayerOrigin = "res://addons/liborng/icons/icon_player_origin.svg"
+PlayerSpawn = "res://addons/liborng/icons/icon_player_spawn.svg"
+SublevelLoader = "res://addons/liborng/icons/icon_sublevel_loader.svg"
+SublevelScene = "res://addons/liborng/icons/icon_sublevel_scene.svg"
diff --git a/orng.gdextension.uid b/orng.gdextension.uid
new file mode 100644
index 0000000..31b45e4
--- /dev/null
+++ b/orng.gdextension.uid
@@ -0,0 +1 @@
+uid://c73yd0haglybk
diff --git a/src/.gdignore b/src/.gdignore
new file mode 100644
index 0000000..e69de29
diff --git a/src/.sconsign.dblite b/src/.sconsign.dblite
new file mode 100644
index 0000000..dc9a5b0
Binary files /dev/null and b/src/.sconsign.dblite differ
diff --git a/src/SConstruct b/src/SConstruct
new file mode 100644
index 0000000..ca6847e
--- /dev/null
+++ b/src/SConstruct
@@ -0,0 +1,10 @@
+env = SConscript("godot-cpp/SConstruct")
+env.Append(CPPPATH="liborng/")
+
+src = Glob('liborng/*.cpp')
+src += Glob('liborng/**/*.cpp')
+src += Glob('liborng/**/**/*.cpp')
+
+libpath = '../{}_{}/{}/liborng{}'.format(env['platform'], env['arch'], env['target'], env['SHLIBSUFFIX'])
+sharedlib = env.SharedLibrary(libpath, src)
+Default(sharedlib)
diff --git a/src/godot-cpp b/src/godot-cpp
new file mode 160000
index 0000000..4bc6e67
--- /dev/null
+++ b/src/godot-cpp
@@ -0,0 +1 @@
+Subproject commit 4bc6e67d51df4c12e150fe4769911e239c627a47
diff --git a/src/liborng/data_assets/desktop.ini b/src/liborng/data_assets/desktop.ini
new file mode 100644
index 0000000..d957fd1
--- /dev/null
+++ b/src/liborng/data_assets/desktop.ini
@@ -0,0 +1,4 @@
+[ViewState]
+Mode=
+Vid=
+FolderType=Generic
diff --git a/src/liborng/data_assets/save_file_data.cpp b/src/liborng/data_assets/save_file_data.cpp
new file mode 100644
index 0000000..f72e6f7
--- /dev/null
+++ b/src/liborng/data_assets/save_file_data.cpp
@@ -0,0 +1,104 @@
+/*
+ * ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
+ *
+ * If this source code makes it to the public internet, this software can be
+ * considered to be protected by the MIT licence. Have fun with it.
+ */
+
+#include "save_file_data.h"
+
+#include
+
+#include
+#include
+using namespace godot;
+
+
+void SaveFileData::init()
+{
+ this->level = ProjectSettings::get_singleton()->get_setting_with_override(FIRST_LEVEL_SETTING);
+}
+
+Dictionary SaveFileData::serialize() const
+{
+ Dictionary dictionary;
+
+ dictionary[KEY_SAVEFILE_LEVEL] = this->level;
+ dictionary[KEY_SAVEFILE_CHECKPOINT] = this->checkpoint;
+ dictionary[KEY_SAVEFILE_DIFFICULTY] = this->difficulty;
+
+ return dictionary;
+}
+
+void SaveFileData::deserialize(const Dictionary &dictionary)
+{
+ this->level = dictionary[KEY_SAVEFILE_LEVEL];
+ this->checkpoint = dictionary[KEY_SAVEFILE_CHECKPOINT];
+ this->difficulty = SaveFileData::DifficultySetting((uint8_t)dictionary[KEY_SAVEFILE_DIFFICULTY]);
+}
+
+
+Dictionary SaveHeaderData::serialize() const
+{
+ Dictionary dictionary;
+
+ dictionary[KEY_HEADER_THUMBNAIL] = this->thumbnail;
+ dictionary[KEY_HEADER_FILENAME] = this->filename;
+ dictionary[KEY_HEADER_DIFFICULTY] = this->difficulty;
+ dictionary[KEY_HEADER_PLAYTIME] = this->playtime;
+ dictionary[KEY_HEADER_LASTSAVEDATE] = this->last_save_date;
+
+ dictionary[KEY_HEADER_LEVEL] = this->level;
+ dictionary[KEY_HEADER_CHECKPOINT] = this->checkpoint;
+
+ return dictionary;
+}
+
+void SaveHeaderData::deserialize(const Dictionary &dictionary)
+{
+ this->thumbnail = dictionary[KEY_HEADER_THUMBNAIL];
+ this->filename = dictionary[KEY_HEADER_FILENAME];
+ this->difficulty = SaveFileData::DifficultySetting((uint8_t)dictionary[KEY_HEADER_DIFFICULTY]);
+ this->playtime = dictionary[KEY_HEADER_PLAYTIME];
+ this->last_save_date = dictionary[KEY_HEADER_LASTSAVEDATE];
+
+ this->level = dictionary[KEY_HEADER_LEVEL];
+ this->checkpoint = dictionary[KEY_HEADER_CHECKPOINT];
+}
+
+
+void SaveFileIndex::update_header(const uint8_t slot, const SaveFileData &save_data)
+{
+ this->headers[slot] = std::make_unique();
+ this->headers[slot]->level = save_data.level;
+ this->headers[slot]->checkpoint = save_data.checkpoint;
+ this->headers[slot]->difficulty = save_data.difficulty;
+}
+
+void SaveFileIndex::clear_slot(const uint8_t slot)
+{
+ this->headers[slot] = std::make_unique();
+}
+
+Dictionary SaveFileIndex::serialize() const
+{
+ Dictionary dictionary;
+
+ for (const std::pair> &header : this->headers)
+ {
+ dictionary[header.first] = header.second->serialize();
+ }
+
+ return dictionary;
+}
+
+void SaveFileIndex::deserialize(const Dictionary &dictionary)
+{
+ const Array &keys = dictionary.keys();
+ for (uint8_t i = 0; i < keys.size(); i++)
+ {
+ const uint8_t &key = keys[i];
+ this->headers[key] = std::make_unique();
+ this->headers[key]->deserialize(dictionary[key]);
+ }
+}
diff --git a/src/liborng/data_assets/save_file_data.h b/src/liborng/data_assets/save_file_data.h
new file mode 100644
index 0000000..f5fc9b6
--- /dev/null
+++ b/src/liborng/data_assets/save_file_data.h
@@ -0,0 +1,116 @@
+/*
+ * ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
+ *
+ * If this source code makes it to the public internet, this software can be
+ * considered to be protected by the MIT licence. Have fun with it.
+ */
+
+#pragma once
+
+#include "save_file_data_keys.h"
+#include "nodes/player_spawn.h"
+
+#include