liborng is now its own repository, as it always should have been.

This commit is contained in:
2026-07-31 00:10:44 -04:00
parent 934a88d99e
commit 0ea02dff8a
74 changed files with 4248 additions and 4 deletions
+6 -4
View File
@@ -11,15 +11,13 @@
# Precompiled Headers # Precompiled Headers
*.gch *.gch
*.pch *.pch
*.pdb
# Compiled Dynamic libraries # Compiled Dynamic libraries
*.so *.so
*.dylib *.dylib
*.dll *.dll
*.exp
# Fortran module files
*.mod
*.smod
# Compiled Static libraries # Compiled Static libraries
*.lai *.lai
@@ -31,4 +29,8 @@
*.exe *.exe
*.out *.out
*.app *.app
*.dll
# ---> Platform Files
*.sh
+4
View File
@@ -0,0 +1,4 @@
[submodule "src/godot-cpp"]
path = src/godot-cpp
url = https://github.com/godotengine/godot-cpp
branch = 4.2
+32
View File
@@ -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")
@@ -0,0 +1 @@
uid://b3bafn47rxsbi
+155
View File
@@ -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
+10
View File
@@ -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("")
@@ -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]
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg1"
sodipodi:docname="icon_hide_in_game.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="53.8125"
inkscape:cx="8.018583"
inkscape:cy="7.7770035"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<circle
fill="none"
stroke-width="2"
stroke="#fc7f7f"
id="circle1"
style="stroke-width:1.99937008;stroke-dasharray:none"
cy="8"
cx="8"
r="5" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 1">
<path
style="fill:#8000ff;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.99937;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="M 3,3 13,13"
id="path1"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+43
View File
@@ -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
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg1"
sodipodi:docname="icon_level_scene.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="38.051184"
inkscape:cx="6.6357988"
inkscape:cy="8.05494"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="house">
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 3,8 8,3 13,8"
id="path1"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 12,7 -1,6 H 5 L 4,7"
id="path2"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+43
View File
@@ -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
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg1"
sodipodi:docname="icon_player_origin.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="38.051184"
inkscape:cx="7.2928086"
inkscape:cy="8.4754262"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 1">
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round"
d="M 4,14 V 2"
id="path1"
sodipodi:nodetypes="cc" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round"
d="M 4,2.75 12,6 4,9.25"
id="path2"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round"
d="m 6,11 v 2 l 3,1 3,-1 V 11 L 9,10 Z"
id="path3"
sodipodi:nodetypes="ccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+43
View File
@@ -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
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg1"
sodipodi:docname="icon_player_spawn.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="38.051184"
inkscape:cx="7.2928086"
inkscape:cy="8.4754262"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 1">
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round"
d="M 4,14 V 2"
id="path1"
sodipodi:nodetypes="cc" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round"
d="M 4,2.75 12,6 4,9.25"
id="path2"
sodipodi:nodetypes="ccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+43
View File
@@ -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
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg1"
sodipodi:docname="icon_sublevel_loader.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="26.90625"
inkscape:cx="5.8536585"
inkscape:cy="4.9245064"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<path
d="M 1,1 V 5 H 3 V 3 H 5 V 1 Z m 10,0 v 2 h 2 v 2 h 2 V 1 Z M 1,11 v 4 H 5 V 13 H 3 v -2 z m 12,0 v 2 h -2 v 2 h 4 v -4 z"
fill="#fc7f7f"
id="path1"
sodipodi:nodetypes="cccccccccccccccccccccccccccc" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 1">
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 3,8 8,3 13,8"
id="path2"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 4,7 1,5 H 8 V 7 h 4"
id="path3"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+43
View File
@@ -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
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg1"
sodipodi:docname="icon_sublevel_scene.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="53.8125"
inkscape:cx="7.8513356"
inkscape:cy="7.9814169"
inkscape:window-width="1920"
inkscape:window-height="1057"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="house">
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 3,8 8,3 13,8"
id="path1"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#fc7f7f;stroke-width:1.999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 12,7 H 8 v 6 H 5 L 4,7"
id="path2"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+43
View File
@@ -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
Binary file not shown.
+63
View File
@@ -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
Binary file not shown.
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
# Blender 3.3.6 MTL File: 'arrow.blend'
# www.blender.org
+25
View File
@@ -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
Binary file not shown.
+20
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
uid://c73yd0haglybk
View File
Binary file not shown.
+10
View File
@@ -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)
Submodule
+1
Submodule src/godot-cpp added at 4bc6e67d51
+4
View File
@@ -0,0 +1,4 @@
[ViewState]
Mode=
Vid=
FolderType=Generic
+104
View File
@@ -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 <algorithm>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
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<SaveHeaderData>();
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<SaveHeaderData>();
}
Dictionary SaveFileIndex::serialize() const
{
Dictionary dictionary;
for (const std::pair<const uint8_t, std::unique_ptr<SaveHeaderData>> &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<SaveHeaderData>();
this->headers[key]->deserialize(dictionary[key]);
}
}
+116
View File
@@ -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 <map>
#include <memory>
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
using namespace godot;
#define FIRST_LEVEL_SETTING "orange_cat/levels/starter_levels/new_save_file_scene"
class SaveManager;
class SaveFileIndex;
class SaveFileSerializerInterface
{
public:
virtual Dictionary serialize() const = 0;
virtual void deserialize(const Dictionary &dictionary) = 0;
};
class SaveFileData : public Object, public SaveFileSerializerInterface
{
GDCLASS(SaveFileData, Object);
friend class SaveManager;
friend class SaveFileIndex;
public:
enum DifficultySetting
{
VERY_EASY,
EASY,
NORMAL,
HARD,
VERY_HARD,
MAX
};
inline static const char *difficulty_names[DifficultySetting::MAX] = { "Very Easy", "Easy", "Normal", "Hard", "Very Hard" };
void init();
virtual Dictionary serialize() const override;
virtual void deserialize(const Dictionary &dictionary) override;
protected:
StringName level;
StringName checkpoint = PLAYER_ORIGIN_TAG;
SaveFileData::DifficultySetting difficulty = SaveFileData::DifficultySetting::NORMAL;
// Godot boilerplate below
protected:
static void _bind_methods()
{
BIND_ENUM_CONSTANT(VERY_EASY);
BIND_ENUM_CONSTANT(EASY);
BIND_ENUM_CONSTANT(NORMAL);
BIND_ENUM_CONSTANT(HARD);
BIND_ENUM_CONSTANT(VERY_HARD);
}
};
VARIANT_ENUM_CAST(SaveFileData::DifficultySetting);
class SaveHeaderData : public SaveFileSerializerInterface
{
friend class SaveFileIndex;
public:
virtual Dictionary serialize() const override;
virtual void deserialize(const Dictionary &dictionary) override;
bool is_empty() const { return this->filename.is_empty(); }
protected:
Ref<Resource> thumbnail;
StringName filename;
SaveFileData::DifficultySetting difficulty = SaveFileData::DifficultySetting::NORMAL;
String playtime;
String last_save_date;
StringName level;
StringName checkpoint;
};
class SaveFileIndex : public Object, public SaveFileSerializerInterface
{
friend class SaveManager;
public:
void update_header(const uint8_t slot, const SaveFileData &save_data);
void clear_slot(const uint8_t slot);
virtual Dictionary serialize() const override;
virtual void deserialize(const Dictionary &dictionary) override;
protected:
std::map<const uint8_t, std::unique_ptr<SaveHeaderData>> headers;
// Godot boilerplate below
protected:
static void _bind_methods() {}
};
@@ -0,0 +1,40 @@
/*
* ©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
// SaveHeaderData
#ifdef DEBUG_ENABLED
#define KEY_HEADER_THUMBNAIL "Thumbnail"
#define KEY_HEADER_FILENAME "Filename"
#define KEY_HEADER_DIFFICULTY "Difficulty"
#define KEY_HEADER_PLAYTIME "Play Time"
#define KEY_HEADER_LASTSAVEDATE "Last Save Date"
#define KEY_HEADER_LEVEL "Level"
#define KEY_HEADER_CHECKPOINT "Checkpoint"
#else
#define KEY_HEADER_THUMBNAIL "🖼️"
#define KEY_HEADER_FILENAME "📄"
#define KEY_HEADER_DIFFICULTY "💪🏼"
#define KEY_HEADER_PLAYTIME "🕓"
#define KEY_HEADER_LASTSAVEDATE "📅"
#define KEY_HEADER_LEVEL "🌆"
#define KEY_HEADER_CHECKPOINT "📍"
#endif // DEBUG_ENABLED
// SaveFileData
#ifdef DEBUG_ENABLED
#define KEY_SAVEFILE_LEVEL "Level"
#define KEY_SAVEFILE_CHECKPOINT "Checkpoint"
#define KEY_SAVEFILE_DIFFICULTY "Difficulty"
#else
#define KEY_SAVEFILE_LEVEL "🌆"
#define KEY_SAVEFILE_CHECKPOINT "📍"
#define KEY_SAVEFILE_DIFFICULTY "💪🏼"
#endif // DEBUG_ENABLED
+123
View File
@@ -0,0 +1,123 @@
/*
* ©2024 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 "nodes/controller_menu.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/property_tweener.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void ControllerMenu::_enter_tree()
{
#ifdef DEBUG_ENABLED
if(!Engine::get_singleton()->is_editor_hint())
{
#endif
this->set_visible(false);
this->set_modulate(Color(1.0f, 1.0f, 1.0f, 0.0f));
#ifdef DEBUG_ENABLED
}
#endif
this->_find_buttons_recursive(const_cast<ControllerMenu*>(this), this->buttons);
// Buttons should default to not being focusable
for (Button *button : this->buttons)
{
button->set_focus_mode(Control::FocusMode::FOCUS_NONE);
}
if (!this->primary_focus_button)
{
this->primary_focus_button = this->buttons.front();
}
if (!this->back_button)
{
this->back_button = this->buttons.back();
}
// We currently expect at least one button
DEV_ASSERT(this->primary_focus_button);
DEV_ASSERT(this->back_button);
}
void ControllerMenu::_input(const Ref<InputEvent> &event)
{
if (event->is_action_type() && this->back_button->get_focus_mode() != Control::FocusMode::FOCUS_NONE)
{
StringName back_action = ProjectSettings::get_singleton()->get_setting("orange_cat/controller_menu/back_action", "ui_cancel");
const bool action_state = this->back_button->get_action_mode() == Button::ACTION_MODE_BUTTON_RELEASE ? event->is_action_released(back_action) : event->is_action_pressed(back_action);
if (action_state)
{
if (this->back_button->has_focus())
{
this->back_button->emit_signal("pressed");
}
else
{
this->back_button->grab_focus();
}
}
}
}
void ControllerMenu::_activate()
{
this->set_visible(true);
this->set_modulate(Color(1.0f, 1.0f, 1.0f, 0.0f));
this->create_tween()->tween_property(this, "modulate", Color(1.0f, 1.0f, 1.0f, 1.0f), 0.25f)->connect("finished", callable_mp(this, &ControllerMenu::_finish_activating));
}
void ControllerMenu::_finish_activating()
{
for (Button *button : this->buttons)
{
button->set_focus_mode(Control::FocusMode::FOCUS_ALL);
}
this->primary_focus_button->grab_focus();
this->emit_signal("activated");
}
void ControllerMenu::_deactivate()
{
for (Button *button : this->buttons)
{
if (button->has_focus())
{
this->primary_focus_button = button;
}
button->set_focus_mode(Control::FocusMode::FOCUS_NONE);
}
this->set_modulate(Color(1.0f, 1.0f, 1.0f, 1.0f));
this->create_tween()->tween_property(this, "modulate", Color(1.0f, 1.0f, 1.0f, 0.0f), 0.25f)->connect("finished", callable_mp(this, &ControllerMenu::_finish_deactivating));
}
void ControllerMenu::_finish_deactivating()
{
this->set_visible(false);
this->emit_signal("deactivated");
}
void ControllerMenu::_find_buttons_recursive(Control *control, std::vector<Button*> &buttons) const
{
const TypedArray<Node> &children = control->get_children();
for (int i = 0; i < children.size(); i++)
{
if (Control *child = cast_to<Control>(children[i]))
{
if (child->is_class("Button"))
{
buttons.push_back(cast_to<Button>(child));
}
this->_find_buttons_recursive(child, buttons);
}
}
}
+67
View File
@@ -0,0 +1,67 @@
/*
* ©2024 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 "orng_macros.h"
#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/input_event.hpp>
using namespace godot;
class ControllerMenu : public Control
{
friend class MenuStack;
GDCLASS(ControllerMenu, Control);
public:
virtual void _enter_tree() override;
virtual void _input(const Ref<InputEvent> &event) override;
void set_primary_focus_button(Button *button) { this->primary_focus_button = button; }
Button *get_primary_focus_button() const { return this->primary_focus_button; }
void set_back_button(Button *button) { this->back_button = button; }
Button *get_back_button() const { return this->back_button; }
protected:
Button *primary_focus_button = nullptr;
Button *back_button = nullptr;
private:
void _activate();
void _finish_activating();
void _deactivate();
void _finish_deactivating();
void _find_buttons_recursive(Control *control, std::vector<Button*> &buttons) const;
std::vector<Button*> buttons;
// Godot boilerplate below
protected:
static void _bind_methods()
{
{ // Functions
ADD_GETTER_SETTER_HINTED(ControllerMenu, primary_focus_button, Variant::OBJECT, PROPERTY_HINT_NODE_TYPE, "Button");
ADD_GETTER_SETTER_HINTED(ControllerMenu, back_button, Variant::OBJECT, PROPERTY_HINT_NODE_TYPE, "Button");
BIND_METHOD(ControllerMenu, _finish_activating);
BIND_METHOD(ControllerMenu, _finish_deactivating);
}
{ // Signals
ADD_SIGNAL(MethodInfo("activated"));
ADD_SIGNAL(MethodInfo("deactivated"));
ADD_SIGNAL(MethodInfo("push_new_menu", PropertyInfo(Variant::OBJECT, "menu", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene")));
ADD_SIGNAL(MethodInfo("pop_menu"));
}
}
};
+13
View File
@@ -0,0 +1,13 @@
/*
* ©2024 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 "nodes/controller_message.h"
using namespace godot;
+41
View File
@@ -0,0 +1,41 @@
/*
* ©2024 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 "nodes/controller_menu.h"
using namespace godot;
class ControllerMessage : public ControllerMenu
{
GDCLASS(ControllerMessage, ControllerMenu);
public:
void set_message_text_two_buttons(StringName message, StringName confirm, StringName cancel) { this->message_text = message; this->confirm_text = confirm; this->cancel_text = cancel; }
void set_message_text_one_button(StringName message, StringName cancel) { this->message_text = message; this->cancel_text = cancel; this->button_count = ControllerMessage::ButtonCount::ONE_BUTTON; }
enum ButtonCount {
ONE_BUTTON,
TWO_BUTTONS
};
protected:
StringName message_text;
StringName confirm_text;
StringName cancel_text;
ControllerMessage::ButtonCount button_count = ControllerMessage::ButtonCount::TWO_BUTTONS;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ClassDB::bind_method(D_METHOD("set_message_text", "message", "confirm", "cancel"), &ControllerMessage::set_message_text_one_button);
ClassDB::bind_method(D_METHOD("set_message_text", "message", "cancel"), &ControllerMessage::set_message_text_two_buttons);
}
};
+36
View File
@@ -0,0 +1,36 @@
/*
* ©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 "hide_in_game.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void HideInGame::_enter_tree()
{
#ifdef DEBUG_ENABLED
if (!Engine::get_singleton()->is_editor_hint())
{
#endif
this->queue_free();
return;
#ifdef DEBUG_ENABLED
}
#endif
Node3D::_enter_tree();
}
void HideInGame::_ready()
{
if (!Engine::get_singleton()->is_editor_hint())
{
return;
}
Node3D::_ready();
}
+25
View File
@@ -0,0 +1,25 @@
/*
* ©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 <godot_cpp/classes/node3d.hpp>
using namespace godot;
class HideInGame : public Node3D
{
GDCLASS(HideInGame, Node3D);
public:
virtual void _enter_tree() override;
virtual void _ready() override;
// Godot boilerplate below
protected:
static void _bind_methods(){}
};
+108
View File
@@ -0,0 +1,108 @@
/*
* ©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 "level_scene.h"
#include "nodes/player_spawn.h"
#include "singletons/save_manager.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void LevelScene::_ready()
{
#ifdef DEBUG_ENABLED
if(Engine::get_singleton()->is_editor_hint()) { return; }
#endif // DEBUG_ENABLED
// We defer this call until the end of the setup frame, since we'll need
// SaveManager to have been registered as a global singleton before we can
// find the correct player spawn. This should not be necessary any time
// after the first scene has loaded and the singleton has been registered,
// but for safety purposes this still makes sense right now.
this->call_deferred("_scene_ready");
}
void LevelScene::_scene_ready()
{
// We should probably assert that there is a valid GameClasses file first
// thing, since that's vital for initialising LevelScenes.
#ifdef DEBUG_ENABLED
if (!this->game_classes.is_valid())
{
UtilityFunctions::push_error("No GameClasses resource attached to level.");
return;
}
#endif
this->level_death_plane_node = cast_to<Area3D>(this->get_node_or_null(this->level_death_plane));
if (this->level_death_plane_node)
{
this->level_death_plane_node->set_monitoring(true);
this->level_death_plane_node->set_gravity_space_override_mode(Area3D::SpaceOverride::SPACE_OVERRIDE_DISABLED);
this->level_death_plane_node->set_linear_damp_space_override_mode(Area3D::SpaceOverride::SPACE_OVERRIDE_DISABLED);
this->level_death_plane_node->set_angular_damp_space_override_mode(Area3D::SpaceOverride::SPACE_OVERRIDE_DISABLED);
this->level_death_plane_node->connect("body_entered", callable_mp(this, &LevelScene::_body_fell_out_of_world));
}
SpawnPointMap spawn_points = this->_find_player_spawns();
const StringName &current_checkpoint = SaveManager::get_singleton()->get_current_checkpoint();
const PlayerOrigin *spawn_point = spawn_points[current_checkpoint];
#ifdef DEBUG_ENABLED
if (!spawn_point)
{
UtilityFunctions::push_error("Could not find a spawn point named ", current_checkpoint, "; defaulting to origin...");
spawn_point = spawn_points[PLAYER_ORIGIN_TAG];
DEV_ASSERT(spawn_point);
}
#endif
// Attempt to spawn the player scene at this spawn point.
spawn_point->spawn_player(this, this->game_classes->get_player_scene());
}
SpawnPointMap LevelScene::_find_player_spawns() const
{
SpawnPointMap spawn_points;
this->_find_player_spawns_recursive(this, spawn_points);
return spawn_points;
}
void LevelScene::_find_player_spawns_recursive(const Node *node, SpawnPointMap &spawn_points) const
{
if (const PlayerOrigin *potential_spawn = cast_to<PlayerOrigin>(node))
{
spawn_points.emplace(potential_spawn->get_tag(), potential_spawn);
}
const TypedArray<Node> &children = node->get_children();
for (int i = 0; i < children.size(); i++)
{
this->_find_player_spawns_recursive(cast_to<Node>(children[i]), spawn_points);
}
}
void LevelScene::_body_fell_out_of_world(Node3D *body)
{
if (body->has_method("on_fell_out_of_world"))
{
body->call("on_fell_out_of_world");
}
else
{
body->queue_free();
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
* ©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 "orng_macros.h"
#include <map>
#include <godot_cpp/classes/area3d.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/resource.hpp>
using namespace godot;
typedef std::map<const class StringName, const class PlayerOrigin*> SpawnPointMap;
class GameClasses : public Resource
{
GDCLASS(GameClasses, Resource);
public:
void set_player_scene(Ref<PackedScene> s) { this->player_scene = s; }
Ref<PackedScene> get_player_scene() const { return this->player_scene; }
protected:
Ref<PackedScene> player_scene;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(GameClasses, player_scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
}
};
class LevelScene : public Node3D
{
GDCLASS(LevelScene, Node3D);
public:
virtual void _ready() override;
protected:
void set_game_classes(Ref<GameClasses> c) { this->game_classes = c; }
Ref<GameClasses> get_game_classes() const { return this->game_classes; }
NodePath level_death_plane;
void set_level_death_plane(NodePath ldp) { this->level_death_plane = ldp; }
NodePath get_level_death_plane() const { return this->level_death_plane; }
Ref<GameClasses> game_classes;
private:
virtual void _scene_ready();
virtual void _body_fell_out_of_world(Node3D *body);
virtual SpawnPointMap _find_player_spawns() const;
void _find_player_spawns_recursive(const Node *node, SpawnPointMap &spawn_points) const;
Area3D *level_death_plane_node = nullptr;
// Godot boilerplate below
protected:
static void _bind_methods()
{
BIND_METHOD(LevelScene, _scene_ready);
ADD_GETTER_SETTER_HINTED(LevelScene, game_classes, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GameClasses");
ADD_GETTER_SETTER_HINTED(LevelScene, level_death_plane, Variant::NODE_PATH, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Area3D");
BIND_METHOD(LevelScene, _body_fell_out_of_world);
}
};
+96
View File
@@ -0,0 +1,96 @@
/*
* ©2024 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 "nodes/menu_stack.h"
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void MenuStack::push_menu(Ref<PackedScene> menu)
{
// Make sure we're not pushing a menu while one is actively being transitioned.
DEV_ASSERT(!this->transition_to_menu);
if (ControllerMenu *menu_node = cast_to<ControllerMenu>(menu->instantiate()))
{
this->transition_to_menu = menu_node;
this->add_child(this->transition_to_menu);
this->menu_stack.push(this->transition_to_menu);
this->transition_to_menu->connect("pop_menu", callable_mp(this, &MenuStack::pop_menu), Object::ConnectFlags::CONNECT_ONE_SHOT);
this->transition_to_menu->connect("push_new_menu", callable_mp(this, &MenuStack::push_menu));
if (this->active_menu)
{
this->active_menu->connect("deactivated", callable_mp(this, &MenuStack::old_menu_deactivated), Object::ConnectFlags::CONNECT_ONE_SHOT);
this->active_menu->_deactivate();
}
else
{
this->start_menu_activation();
}
}
#ifdef DEBUG_ENABLED
else
{
UtilityFunctions::push_error("Invalid menu pushed to stack.");
}
#endif
}
void MenuStack::old_menu_deactivated()
{
this->finish_menu_deactivation();
this->start_menu_activation();
}
void MenuStack::pushed_menu_activated()
{
this->active_menu = this->transition_to_menu;
this->transition_to_menu = nullptr;
}
void MenuStack::pop_menu()
{
this->menu_stack.pop();
this->active_menu->connect("deactivated", callable_mp(this, &MenuStack::pushed_menu_deactivated), Object::ConnectFlags::CONNECT_ONE_SHOT);
this->active_menu->_deactivate();
}
void MenuStack::pushed_menu_deactivated()
{
this->remove_child(this->active_menu);
this->active_menu->queue_free();
this->finish_menu_deactivation();
if (this->transition_to_menu = this->menu_stack.top())
{
this->start_menu_activation();
}
}
void MenuStack::push_message_one_button(StringName message, StringName cancel_text, Callable cancel_callback)
{
if (this->message = cast_to<ControllerMessage>(this->message_scene->instantiate()))
{
this->message->set_message_text_one_button(message, cancel_text);
this->start_message_activation();
}
}
void MenuStack::push_message_two_buttons(StringName message, StringName confirm_text, StringName cancel_text, Callable confirm_callback, Callable cancel_callback)
{
if (this->message = cast_to<ControllerMessage>(this->message_scene->instantiate()))
{
this->message->set_message_text_two_buttons(message, confirm_text, cancel_text);
this->start_message_activation();
}
}
void MenuStack::pop_message()
{
this->finish_message_deactivation();
}
+88
View File
@@ -0,0 +1,88 @@
/*
* ©2024 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 "orng_macros.h"
#include <stack>
#include "nodes/controller_menu.h"
#include "nodes/controller_message.h"
#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
using namespace godot;
class MenuStack : public Control
{
GDCLASS(MenuStack, Control);
public:
void push_menu(Ref<PackedScene> menu);
void pop_menu();
void push_message_one_button(StringName message, StringName cancel_text, Callable cancel_callback = Callable());
void push_message_two_buttons(StringName message, StringName confirm_text, StringName cancel_text, Callable confirm_callback = Callable(), Callable cancel_callback = Callable());
void pop_message();
void set_message_scene(Ref<PackedScene> scene) { this->message_scene = scene; }
Ref<PackedScene> get_message_scene() const { return this->message_scene; }
ControllerMenu *get_active_menu() const { return this->active_menu; }
protected:
Ref<PackedScene> message_scene;
private:
void pushed_menu_activated();
void old_menu_deactivated();
void pushed_menu_deactivated();
void start_menu_activation()
{
this->transition_to_menu->connect("activated", callable_mp(this, &MenuStack::pushed_menu_activated), Object::ConnectFlags::CONNECT_ONE_SHOT);
this->transition_to_menu->_activate();
}
void finish_menu_deactivation()
{
this->active_menu = nullptr;
}
void start_message_activation()
{
this->message->connect("activated", callable_mp(this, &MenuStack::pushed_menu_activated), Object::ConnectFlags::CONNECT_ONE_SHOT);
this->message->_activate();
}
void finish_message_deactivation()
{
this->message = nullptr;
}
ControllerMenu *active_menu = nullptr;
ControllerMenu *transition_to_menu = nullptr;
std::stack<ControllerMenu*> menu_stack;
ControllerMessage *message = nullptr;
// Godot boilerplate below
protected:
static void _bind_methods()
{
BIND_METHOD_1PARAM(MenuStack, push_menu, menu);
BIND_METHOD(MenuStack, pop_menu);
BIND_METHOD(MenuStack, get_active_menu);
BIND_METHOD(MenuStack, pushed_menu_activated);
BIND_METHOD(MenuStack, pushed_menu_deactivated);
BIND_METHOD(MenuStack, old_menu_deactivated);
ADD_GETTER_SETTER_HINTED(MenuStack, message_scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
}
};
+188
View File
@@ -0,0 +1,188 @@
/*
* ©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 "movement_handler.h"
#include <godot_cpp/classes/camera3d.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/viewport.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void MovementHandler::_ready()
{
#ifdef DEBUG_ENABLED
if (!Engine::get_singleton()->is_editor_hint())
{
#endif // DEBUG_ENABLED
// If we haven't had a state chart assigned, try to find it as a direct child.
this->state_chart_node = this->get_node_or_null(this->state_chart);
if (!this->state_chart_node)
{
const TypedArray<Node> &children = this->get_children();
for(int i = 0; i < children.size(); i++)
{
Node *child = cast_to<Node>(children[i]);
if (child->is_class("StateChart"))
{
this->state_chart_node = child;
break;
}
}
if (!this->state_chart_node)
{
UtilityFunctions::push_error("No state chart assigned, and none could be found.");
}
}
this->controlled_body_node = cast_to<CharacterBody3D>(this->get_node_or_null(this->controlled_body));
if (!this->controlled_body_node)
{
this->controlled_body_node = this->cast_to<CharacterBody3D>(this->get_parent());
if(!this->controlled_body_node)
{
UtilityFunctions::push_error("No controlled body assigned, and none could be found.");
}
}
const ProjectSettings *settings = ProjectSettings::get_singleton();
this->gravity = float(settings->get_setting("physics/3d/default_gravity")) * this->gravity_multiplier;
#ifdef DEBUG_ENABLED
}
#endif // DEBUG_ENABLED
}
// Walking processing
void MovementHandler::_on_walking_state_entered()
{
this->emit_signal("state_changed", "walk");
}
void MovementHandler::_on_walking_state_physics_processing(const double _delta)
{
if (!this->controlled_body_node->is_on_floor())
{
this->state_chart_node->call("send_event", "fall");
}
this->_calculate_lateral_velocity(_delta, ground_acceleration, ground_friction);
// Emit the new speed as a range between 0.0 and 1.0 for animation purposes
this->emit_signal("movement_speed_changed", "walk", this->controlled_body_node->get_velocity().length() / top_speed);
}
// Airborne processing
void MovementHandler::_on_airborne_state_entered()
{
this->emit_signal("state_changed", "fall");
}
void MovementHandler::_on_airborne_state_physics_processing(const double _delta)
{
if (this->controlled_body_node->is_on_floor())
{
this->state_chart_node->call("send_event", "stop_falling");
}
this->_calculate_lateral_velocity(_delta, air_acceleration, air_friction);
}
// Jumping processing
void MovementHandler::_on_jumping_state_entered()
{
Vector3 controlled_body_velocity = this->controlled_body_node->get_velocity();
controlled_body_velocity.y = this->jump_force;
this->controlled_body_node->set_velocity(controlled_body_velocity);
this->emit_signal("state_changed", "jump");
}
void MovementHandler::_on_cancel_jump_state_entered()
{
Vector3 controlled_body_velocity = this->controlled_body_node->get_velocity();
if (controlled_body_velocity.y > 0.0)
{
controlled_body_velocity.y /= this->jump_cancel_force;
this->controlled_body_node->set_velocity(controlled_body_velocity);
}
}
void MovementHandler::_on_double_jump_state_entered()
{
Vector3 controlled_body_velocity = this->controlled_body_node->get_velocity();
controlled_body_velocity.y = this->double_jump_force;
this->controlled_body_node->set_velocity(controlled_body_velocity);
this->emit_signal("state_changed", "double_jump");
}
// Common physics (stateless)
void MovementHandler::_physics_process(double _delta)
{
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint())
{
return;
}
#endif // DEBUG_ENABLED
if(!this->controlled_body_node)
{
UtilityFunctions::push_error("No controlled body has been found, so the physics process can't run.");
return;
}
Vector3 velocity = this->controlled_body_node->get_velocity();
velocity.y -= this->gravity * _delta;
this->controlled_body_node->set_velocity(velocity);
this->controlled_body_node->move_and_slide();
}
void MovementHandler::_calculate_lateral_velocity(double _delta, float _acceleration, float _friction)
{
Vector3 controlled_body_velocity = this->controlled_body_node->get_velocity();
const float stored_y_velocity = controlled_body_velocity.y;
Camera3D *active_camera = this->get_viewport()->get_camera_3d();
const Vector3 rotated_movement_vector = this->current_movement_vector.rotated(this->controlled_body_node->get_transform().get_basis()[Vector3::Axis::AXIS_Y].normalized(), active_camera->get_global_rotation().y);
// If the character is accelerating based on our current joystick input,
// lerp with acceleration value. Otherwise, use friction to decelerate.
const float lerp_weight = ((rotated_movement_vector.length() * top_speed) > controlled_body_velocity.length()) ? _acceleration : _friction;
controlled_body_velocity = controlled_body_velocity.lerp(rotated_movement_vector * (top_speed * rotated_movement_vector.length()), lerp_weight * _delta);
controlled_body_velocity.y = stored_y_velocity;
this->controlled_body_node->set_velocity(controlled_body_velocity);
}
// Input response functions
void MovementHandler::_on_movement_2d(float _delta, Vector2 _vector)
{
this->current_movement_vector = Vector3(_vector.x, 0.0, _vector.y);
}
void MovementHandler::_on_jump_pressed()
{
this->state_chart_node->call("send_event", "jump");
}
void MovementHandler::_on_jump_released()
{
this->state_chart_node->call("send_event", "stop_jumping");
}
+157
View File
@@ -0,0 +1,157 @@
/*
* ©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 "orng_macros.h"
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/node.hpp>
using namespace godot;
class MovementHandler : public Node
{
GDCLASS(MovementHandler, Node);
public:
virtual void _ready() override;
virtual void _physics_process(double _delta) override;
protected:
virtual void _on_walking_state_entered();
virtual void _on_walking_state_physics_processing(const double _delta);
virtual void _on_airborne_state_entered();
virtual void _on_airborne_state_physics_processing(const double _delta);
virtual void _on_jumping_state_entered();
virtual void _on_cancel_jump_state_entered();
virtual void _on_double_jump_state_entered();
virtual void _on_movement_2d(float _delta, Vector2 _vector);
virtual void _on_jump_pressed();
virtual void _on_jump_released();
NodePath controlled_body;
void set_controlled_body(NodePath cb) { this->controlled_body = cb; }
NodePath get_controlled_body() const { return this->controlled_body; }
NodePath state_chart;
void set_state_chart(NodePath sc) { this->state_chart = sc; }
NodePath get_state_chart() const { return this->state_chart; }
float top_speed = 6.0f;
void set_top_speed(float s) { this->top_speed = s; }
float get_top_speed() const { return this->top_speed; }
float ground_acceleration = 10.0f;
void set_ground_acceleration(float a) { this->ground_acceleration = a; }
float get_ground_acceleration() const { return this->ground_acceleration; }
float ground_friction = 10.0f;
void set_ground_friction(float f) { this->ground_friction = f; }
float get_ground_friction() const { return this->ground_friction; }
float gravity_multiplier = 4.0f;
void set_gravity_multiplier(float m) { this->gravity_multiplier = m; }
float get_gravity_multiplier() const { return this->gravity_multiplier; }
float air_acceleration = 5.0f;
void set_air_acceleration(float a) { this->air_acceleration = a; }
float get_air_acceleration() const { return this->air_acceleration; }
float air_friction = 5.0f;
void set_air_friction(float f) { this->air_friction = f; }
float get_air_friction() const { return this->air_friction; }
float jump_force = 12.0f;
void set_jump_force(float f) { this->jump_force = f; }
float get_jump_force() const { return this->jump_force; }
float jump_cancel_force = 2.0f;
void set_jump_cancel_force(float c) { this->jump_cancel_force = c; }
float get_jump_cancel_force() const { return this->jump_cancel_force; }
float double_jump_force = 12.0f;
void set_double_jump_force(float f) { this->double_jump_force = f; }
float get_double_jump_force() const { return this->double_jump_force; }
Vector3 jump_stretch_size = Vector3(0.8f, 1.2f, 0.8f);
void set_jump_stretch_size(Vector3 s) { this->jump_stretch_size = s; }
Vector3 get_jump_stretch_size() const { return this->jump_stretch_size; }
private:
void _calculate_lateral_velocity(double _delta, float _acceleration, float _friction);
CharacterBody3D *controlled_body_node = nullptr;
Node3D *camera_boom_node = nullptr;
Node *state_chart_node = nullptr;
float gravity;
Vector3 current_movement_vector;
// Godot boilerplate below
protected:
static void _bind_methods()
{
// State chart signal responses
BIND_METHOD(MovementHandler, _on_walking_state_entered);
BIND_METHOD_1PARAM(MovementHandler, _on_walking_state_physics_processing, delta);
BIND_METHOD(MovementHandler, _on_airborne_state_entered);
BIND_METHOD_1PARAM(MovementHandler, _on_airborne_state_physics_processing, delta);
BIND_METHOD(MovementHandler, _on_jumping_state_entered);
BIND_METHOD(MovementHandler, _on_cancel_jump_state_entered);
BIND_METHOD(MovementHandler, _on_double_jump_state_entered);
BIND_METHOD_2PARAM(MovementHandler, _on_movement_2d, delta, vector);
BIND_METHOD(MovementHandler, _on_jump_pressed);
BIND_METHOD(MovementHandler, _on_jump_released);
// Movement Properties
ADD_GROUP("Ground", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, top_speed, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_SUBGROUP("Acceleration", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, ground_acceleration, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_SUBGROUP("Deceleration", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, ground_friction, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_GROUP("Air", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, gravity_multiplier, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.0,10.0,0.1");
ADD_SUBGROUP("Acceleration", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, air_acceleration, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_SUBGROUP("Deceleration", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, air_friction, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_SUBGROUP("Jumping", "Movement Properties");
ADD_GETTER_SETTER_HINTED(MovementHandler, jump_force, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_GETTER_SETTER_HINTED(MovementHandler, jump_cancel_force, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_GETTER_SETTER_HINTED(MovementHandler, double_jump_force, Variant::FLOAT, PROPERTY_HINT_RANGE, "0.1,20.0,0.01");
ADD_GROUP("Game Juice", "Movement Properties");
ADD_GETTER_SETTER(MovementHandler, jump_stretch_size, Variant::VECTOR3);
// Node references
ADD_GETTER_SETTER_HINTED(MovementHandler, controlled_body, Variant::NODE_PATH, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CharacterBody3D");
ADD_GETTER_SETTER_HINTED(MovementHandler, state_chart, Variant::NODE_PATH, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "StateChart");
// Signals
ADD_SIGNAL(MethodInfo("state_changed", PropertyInfo(Variant::STRING_NAME, "state")));
ADD_SIGNAL(MethodInfo("movement_speed_changed", PropertyInfo(Variant::STRING_NAME, "state"), PropertyInfo(Variant::FLOAT, "speed")));
}
};
+72
View File
@@ -0,0 +1,72 @@
/*
* ©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 "player_spawn.h"
#include "nodes/sublevel_scene.h"
#include "singletons/save_manager.h"
#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/capsule_shape3d.hpp>
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/collision_shape3d.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/material.hpp>
#include <godot_cpp/classes/mesh.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/classes/sprite3d.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
PlayerSpawn::PlayerSpawn()
{
this->tag = "spawn_name";
}
void PlayerSpawn::_on_area_3d_body_entered(Node3D *body)
{
SaveManager *save_manager = SaveManager::get_singleton();
if (save_manager->get_current_checkpoint() != this->tag)
{
save_manager->set_new_checkpoint(this->tag);
}
this->queue_free();
}
bool PlayerOrigin::spawn_player(Node3D *parent, Ref<PackedScene> player) const
{
Ref<PackedScene> player_scene_to_unpack = this->player_scene.is_valid() ? this->player_scene : player;
if(player_scene_to_unpack.is_valid())
{
for (int i = 0; i < this->sublevel_scenes_to_load.size(); i++)
{
if (SublevelScene *sublevel = cast_to<SublevelScene>(this->get_node_or_null(this->sublevel_scenes_to_load[i])))
{
sublevel->load_sublevel();
}
#ifdef DEBUG_ENABLED
else
{
UtilityFunctions::push_error("Empty sublevel in PlayerSpawn object \"", this->tag, "\"");
}
#endif
}
Node3D *player_scene = cast_to<Node3D>(player_scene_to_unpack->instantiate());
player_scene->set_global_transform(this->get_global_transform());
parent->add_child(player_scene);
return true;
}
return false;
}
+81
View File
@@ -0,0 +1,81 @@
/*
* ©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 "orng_macros.h"
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
using namespace godot;
#define PLAYER_ORIGIN_TAG "origin"
// Origin of the level. Player will spawn here by default. This is the parent
// to all other spawn points to ensure the tag can be set to the origin tag by
// default, and only changed by derived classes.
class PlayerOrigin : public Node3D
{
GDCLASS(PlayerOrigin, Node3D);
public:
// Spawn the player at the spawn point's location. If the spawn point has
// no player scene set, the passed in player scene will be used. Function
// returns false if no player could be spawned.
bool spawn_player(Node3D *parent, Ref<PackedScene> player) const;
virtual StringName get_tag() const { return this->tag; }
void set_player_scene(Ref<PackedScene> s) { this->player_scene = s; }
Ref<PackedScene> get_player_scene() const { return this->player_scene; }
void set_sublevel_scenes_to_load(TypedArray<NodePath> scenes) { this->sublevel_scenes_to_load = scenes; }
TypedArray<NodePath> get_sublevel_scenes_to_load() const { return this->sublevel_scenes_to_load; }
protected:
StringName tag = StringName(PLAYER_ORIGIN_TAG);
// Scene to spawn at this location when requested.
Ref<PackedScene> player_scene;
// List of sublevels that need to be loaded along with the player.
TypedArray<NodePath> sublevel_scenes_to_load;
// Godot boilerplate below
protected:
static void _bind_methods()
{
BIND_METHOD(PlayerOrigin, get_tag);
ADD_GETTER_SETTER_HINTED(PlayerOrigin, player_scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
ADD_GETTER_SETTER_HINTED(PlayerOrigin, sublevel_scenes_to_load, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::NODE_PATH, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "SublevelScene"));
}
};
class PlayerSpawn : public PlayerOrigin
{
GDCLASS(PlayerSpawn, PlayerOrigin);
public:
PlayerSpawn();
virtual void set_tag(StringName t) { this->tag = t; }
protected:
virtual void _on_area_3d_body_entered(class Node3D *body);
// Godot boilerplate below
protected:
static void _bind_methods()
{
BIND_METHOD_1PARAM(PlayerSpawn, set_tag, tag);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tag"), "set_tag", "get_tag");
BIND_METHOD_1PARAM(PlayerSpawn, _on_area_3d_body_entered, body);
}
};
+38
View File
@@ -0,0 +1,38 @@
/*
* ©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 "sublevel_loader.h"
SublevelLoader::SublevelLoader()
{
this->set_collision_layer(0b0001);
this->set_collision_mask(0b1000);
}
void SublevelLoader::_ready()
{
this->connect("body_entered", callable_mp(this, &SublevelLoader::_on_sublevel_loader_body_entered), ConnectFlags::CONNECT_ONE_SHOT);
}
void SublevelLoader::_on_sublevel_loader_body_entered(Node3D *body)
{
// First unload sublevels as necessary...
for(int i = 0; i < this->sublevel_scenes_to_unload.size(); i++)
{
cast_to<SublevelScene>(this->get_node_or_null(this->sublevel_scenes_to_unload[i]))->unload_sublevel();
}
// ...and then once that's done, load the ones we want to load.
// We do it in this order because I don't know. It just feels right.
for(int i = 0; i < this->sublevel_scenes_to_load.size(); i++)
{
cast_to<SublevelScene>(this->get_node_or_null(this->sublevel_scenes_to_load[i]))->load_sublevel();
}
this->queue_free();
}
+48
View File
@@ -0,0 +1,48 @@
/*
* ©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 "orng_macros.h"
#include <godot_cpp/classes/area3d.hpp>
#include "sublevel_scene.h"
using namespace godot;
// Scene that loads a subscene immediately in editor, but only on command
// during gameplay.
class SublevelLoader : public Area3D
{
GDCLASS(SublevelLoader, Area3D);
public:
SublevelLoader();
virtual void _ready() override;
void set_sublevel_scenes_to_load(TypedArray<NodePath> scenes) { this->sublevel_scenes_to_load = scenes; }
TypedArray<NodePath> get_sublevel_scenes_to_load() const { return this->sublevel_scenes_to_load; }
void set_sublevel_scenes_to_unload(TypedArray<NodePath> scenes) { this->sublevel_scenes_to_unload = scenes; }
TypedArray<NodePath> get_sublevel_scenes_to_unload() const { return this->sublevel_scenes_to_unload; }
protected:
void _on_sublevel_loader_body_entered(Node3D *body);
TypedArray<NodePath> sublevel_scenes_to_load;
TypedArray<NodePath> sublevel_scenes_to_unload;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(SublevelLoader, sublevel_scenes_to_load, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::NODE_PATH, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "SublevelScene"));
ADD_GETTER_SETTER_HINTED(SublevelLoader, sublevel_scenes_to_unload, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::NODE_PATH, PROPERTY_HINT_NODE_PATH_VALID_TYPES, "SublevelScene"));
BIND_METHOD_1PARAM(SublevelLoader, _on_sublevel_loader_body_entered, body);
}
};
+67
View File
@@ -0,0 +1,67 @@
/*
* ©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 "sublevel_scene.h"
#include "singletons/scene_loader.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void SublevelScene::_ready()
{
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint())
{
this->load_sublevel();
}
#endif // DEBUG_ENABLED
}
void SublevelScene::set_sublevel(StringName s)
{
this->sublevel = s;
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint())
{
this->unload_sublevel();
this->load_sublevel();
}
#endif // DEBUG_ENABLED
}
void SublevelScene::load_sublevel()
{
if (this->sublevel_node)
return;
if (SceneLoader *scene_loader = SceneLoader::get_singleton())
{
scene_loader->load_scene(this->sublevel, callable_mp(this, &SublevelScene::_threaded_load_callback));
}
}
void SublevelScene::_threaded_load_callback(Ref<PackedScene> loaded_scene)
{
if (loaded_scene.is_valid())
{
this->sublevel_node = cast_to<Node3D>(loaded_scene->instantiate());
this->add_child(this->sublevel_node);
}
}
void SublevelScene::unload_sublevel()
{
if (this->sublevel_node)
{
this->sublevel_node->queue_free();
this->sublevel_node = nullptr;
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* ©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 "orng_macros.h"
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
using namespace godot;
// Scene that loads a subscene immediately in editor, but only on command
// during gameplay.
class SublevelScene : public Node3D
{
GDCLASS(SublevelScene, Node3D);
public:
virtual void _ready() override;
void set_sublevel(StringName s);
StringName get_sublevel() const { return this->sublevel; }
void load_sublevel();
void unload_sublevel();
protected:
StringName sublevel;
private:
void _threaded_load_callback(Ref<PackedScene> loaded_scene);
Node3D *sublevel_node = nullptr;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(SublevelScene, sublevel, Variant::STRING, PROPERTY_HINT_FILE, "*.scn,*.tscn");
BIND_METHOD(SublevelScene, load_sublevel);
BIND_METHOD(SublevelScene, unload_sublevel);
}
};
+30
View File
@@ -0,0 +1,30 @@
#pragma once
/**
* Method and property binding helpers
*/
#define BIND_METHOD(Class, MethodName) \
ClassDB::bind_method(D_METHOD(#MethodName), &Class::##MethodName)
#define BIND_METHOD_1PARAM(Class, MethodName, P1) \
ClassDB::bind_method(D_METHOD(#MethodName, #P1), &Class::##MethodName)
#define BIND_METHOD_2PARAM(Class, MethodName, P1, P2) \
ClassDB::bind_method(D_METHOD(#MethodName, #P1, #P2), &Class::##MethodName)
#define BIND_METHOD_3PARAM(Class, MethodName, P1, P2, P3) \
ClassDB::bind_method(D_METHOD(#MethodName, #P1, #P2, #P3), &Class::##MethodName)
#define ADD_GETTER_SETTER(Class, Property, VariantType) \
ClassDB::bind_method(D_METHOD("get_"#Property), &Class::get_##Property); \
ClassDB::bind_method(D_METHOD("set_"#Property, #Property), &Class::set_##Property); \
ADD_PROPERTY(PropertyInfo(VariantType, #Property), "set_"#Property, "get_"#Property)
#define ADD_GETTER_SETTER_HINTED(Class, Property, VariantType, Hint, HintString) \
ClassDB::bind_method(D_METHOD("get_"#Property), &Class::get_##Property); \
ClassDB::bind_method(D_METHOD("set_"#Property, #Property), &Class::set_##Property); \
ADD_PROPERTY(PropertyInfo(VariantType, #Property, Hint, HintString), "set_"#Property, "get_"#Property)
/**
* Deferred function helpers
*/
#define CALL_NEXT_FRAME(C) \
this->get_tree()->create_timer(0.001)->connect("timeout", C)
+102
View File
@@ -0,0 +1,102 @@
/* godot-cpp integration testing project.
*
* This is free and unencumbered software released into the public domain.
*/
#include "register_types.h"
#include <gdextension_interface.h>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/godot.hpp>
#include "nodes/controller_menu.h"
#include "nodes/hide_in_game.h"
#include "nodes/level_scene.h"
#include "nodes/menu_stack.h"
#include "nodes/movement_handler.h"
#include "nodes/player_spawn.h"
#include "nodes/sublevel_loader.h"
#include "nodes/sublevel_scene.h"
#include "resources/level_metadata_map.h"
#include "resources/level_metadata_resource.h"
#include "singletons/input_handler.h"
#include "singletons/save_manager.h"
#include "singletons/scene_loader.h"
#include "singletons/vendor_service/vendor_service.h"
using namespace godot;
GDSINGLETON_REGISTER_PTR(InputHandler, _input_handler_singleton);
GDSINGLETON_REGISTER_PTR(SaveManager, _save_manager_singleton);
GDSINGLETON_REGISTER_PTR(SceneLoader, _scene_loader_singleton);
GDSINGLETON_REGISTER_PTR(VendorService, _vendor_service_singleton);
void initialize_orng_module(ModuleInitializationLevel p_level) {
if (p_level == ModuleInitializationLevel::MODULE_INITIALIZATION_LEVEL_SCENE)
{
ClassDB::register_class<HideInGame>();
ClassDB::register_class<ControllerMenu>();
ClassDB::register_class<MenuStack>();
ClassDB::register_class<SublevelLoader>();
ClassDB::register_class<SublevelScene>();
ClassDB::register_class<InputAction>();
ClassDB::register_class<InputAxis>();
ClassDB::register_class<InputAxis2D>();
ClassDB::register_class<InputResource>();
ClassDB::register_class<LevelMetadataMap>();
ClassDB::register_class<LevelMetadataMapEntry>();
ClassDB::register_class<LevelMetadataResource>();
ClassDB::register_class<MovementHandler>();
ClassDB::register_class<GameClasses>();
ClassDB::register_class<LevelScene>();
ClassDB::register_class<PlayerOrigin>();
ClassDB::register_class<PlayerSpawn>();
ClassDB::register_class<SaveFileData>();
GDSINGLETON_REGISTER_CLASS(InputHandler, _input_handler_singleton);
GDSINGLETON_REGISTER_CLASS(SaveManager, _save_manager_singleton);
GDSINGLETON_REGISTER_CLASS(SceneLoader, _scene_loader_singleton);
GDSINGLETON_REGISTER_CLASS(VendorService, _vendor_service_singleton);
}
}
void uninitialize_orng_module(ModuleInitializationLevel p_level) {
if (p_level == ModuleInitializationLevel::MODULE_INITIALIZATION_LEVEL_SCENE)
{
if (Engine::get_singleton()->get_main_loop())
{
GDSINGLETON_UNREGISTER_CLASS(_vendor_service_singleton);
GDSINGLETON_UNREGISTER_CLASS(_scene_loader_singleton);
GDSINGLETON_UNREGISTER_CLASS(_save_manager_singleton);
GDSINGLETON_UNREGISTER_CLASS(_input_handler_singleton);
}
}
}
extern "C" {
// Initialization.
GDExtensionBool GDE_EXPORT orng_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_orng_module);
init_obj.register_terminator(uninitialize_orng_module);
init_obj.set_minimum_library_initialization_level(ModuleInitializationLevel::MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}
+15
View File
@@ -0,0 +1,15 @@
/* godot-cpp integration testing project.
*
* This is free and unencumbered software released into the public domain.
*/
#ifndef _REGISTER_TYPES_H__
#define _REGISTER_TYPES_H__
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void initialize_orng_module(ModuleInitializationLevel p_level);
void uninitialize_orng_module(ModuleInitializationLevel p_level);
#endif // _REGISTER_TYPES_H__
+4
View File
@@ -0,0 +1,4 @@
[ViewState]
Mode=
Vid=
FolderType=Generic
+101
View File
@@ -0,0 +1,101 @@
/*
* ©2024 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 "resources/input_resource.h"
#include <godot_cpp/classes/input.hpp>
using namespace godot;
void InputResource::update_buttons(const Ref<InputEvent> event, const float delta)
{
for (uint8_t i = 0; i < this->input_actions.size(); i++)
{
const InputAction *action = cast_to<InputAction>(this->input_actions[i]);
const StringName suffix_pressed = StringName("_on_") + action->get_action_name() + INPUTACTION_SUFFIX_PRESSED;
if (event->is_action_pressed(action->get_action_name()) && this->has_signal(suffix_pressed))
this->emit_signal(suffix_pressed);
const StringName suffix_released = StringName("_on_") + action->get_action_name() + INPUTACTION_SUFFIX_RELEASED;
if (event->is_action_released(action->get_action_name()) && this->has_signal(suffix_released))
this->emit_signal(suffix_released);
}
}
void InputResource::update_axes(const float delta)
{
for (uint8_t i = 0; i < this->input_axes.size(); i++)
{
const InputAxis *axis = cast_to<InputAxis>(this->input_axes[i]);
const StringName prefix = StringName("_on_") + axis->get_axis_name();
const StringName signal_1d = prefix + INPUTAXIS_ONE_DIMENSIONAL_SUFFIX;
if (this->has_signal(signal_1d)) this->emit_signal(signal_1d, delta, axis->get_x_axis());
const StringName signal_2d = prefix + INPUTAXIS_TWO_DIMENSIONAL_SUFFIX;
if (this->has_signal(signal_2d)) this->emit_signal(signal_2d, delta, axis->get_lateral_vector());
const StringName signal_3d = prefix + INPUTAXIS_THREE_DIMENSIONAL_SUFFIX;
if (this->has_signal(signal_3d)) this->emit_signal(signal_3d, delta, axis->get_spherical_vector());
}
}
void InputResource::update_axes_mouse_event(const Ref<InputEventMouseMotion> event, const float delta)
{
for (uint8_t i = 0; i < this->input_axes.size(); i++)
{
const InputAxis *axis = cast_to<InputAxis>(this->input_axes[i]);
const StringName signal = StringName("_on_") + axis->get_axis_name() + INPUTAXIS_TWO_DIMENSIONAL_SUFFIX;
if (axis->get_include_mouse() && this->has_signal(signal))
this->emit_signal(signal, delta, Vector2(event->get_relative().x, event->get_relative().y) * 0.001 / delta);
}
}
Vector3 InputAxis::get_spherical_vector() const
{
const Input *input = Input::get_singleton();
return Vector3(
input->get_axis(this->axis_name + INPUTAXIS_LEFT_SUFFIX, this->axis_name + INPUTAXIS_RIGHT_SUFFIX),
input->get_axis(this->axis_name + INPUTAXIS_DOWN_SUFFIX, this->axis_name + INPUTAXIS_UP_SUFFIX),
input->get_axis(this->axis_name + INPUTAXIS_FORWARD_SUFFIX, this->axis_name + INPUTAXIS_BACK_SUFFIX)
);
}
Vector2 InputAxis::get_lateral_vector() const
{
return Input::get_singleton()->get_vector(
this->axis_name + INPUTAXIS_LEFT_SUFFIX, this->axis_name + INPUTAXIS_RIGHT_SUFFIX,
this->axis_name + INPUTAXIS_FORWARD_SUFFIX, this->axis_name + INPUTAXIS_BACK_SUFFIX
);
}
Vector2 InputAxis::get_lateral_vector_square() const
{
const Input *input = Input::get_singleton();
return Vector2(
input->get_axis(this->axis_name + INPUTAXIS_LEFT_SUFFIX, this->axis_name + INPUTAXIS_RIGHT_SUFFIX),
input->get_axis(this->axis_name + INPUTAXIS_FORWARD_SUFFIX, this->axis_name + INPUTAXIS_BACK_SUFFIX)
);
}
float InputAxis::get_x_axis() const
{
return Input::get_singleton()->get_axis(this->axis_name + INPUTAXIS_LEFT_SUFFIX, this->axis_name + INPUTAXIS_RIGHT_SUFFIX);
}
float InputAxis::get_y_axis() const
{
return Input::get_singleton()->get_axis(this->axis_name + INPUTAXIS_DOWN_SUFFIX, this->axis_name + INPUTAXIS_UP_SUFFIX);
}
float InputAxis::get_z_axis() const
{
return Input::get_singleton()->get_axis(this->axis_name + INPUTAXIS_FORWARD_SUFFIX, this->axis_name + INPUTAXIS_BACK_SUFFIX);
}
+208
View File
@@ -0,0 +1,208 @@
/*
* ©2024 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 "orng_macros.h"
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
#include <godot_cpp/classes/resource.hpp>
using namespace godot;
#define INPUTACTION_SUFFIX_PRESSED StringName("_pressed")
#define INPUTACTION_SUFFIX_RELEASED StringName("_released")
#define INPUTAXIS_UP_SUFFIX StringName("_up")
#define INPUTAXIS_DOWN_SUFFIX StringName("_down")
#define INPUTAXIS_LEFT_SUFFIX StringName("_left")
#define INPUTAXIS_RIGHT_SUFFIX StringName("_right")
#define INPUTAXIS_FORWARD_SUFFIX StringName("_forward")
#define INPUTAXIS_BACK_SUFFIX StringName("_back")
#define INPUTAXIS_ONE_DIMENSIONAL_SUFFIX StringName("_1d")
#define INPUTAXIS_TWO_DIMENSIONAL_SUFFIX StringName("_2d")
#define INPUTAXIS_THREE_DIMENSIONAL_SUFFIX StringName("_3d")
class InputAxis2D : public Resource
{
GDCLASS(InputAxis2D, Resource);
public:
void set_negative(Ref<InputEvent> n) { this->negative = n; }
Ref<InputEvent> get_negative() const { return this->negative; }
void set_positive(Ref<InputEvent> p) { this->positive = p; }
Ref<InputEvent> get_positive() const { return this->positive; }
protected:
Ref<InputEvent> negative;
Ref<InputEvent> positive;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(InputAxis2D, negative, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputEvent");
ADD_GETTER_SETTER_HINTED(InputAxis2D, positive, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputEvent");
}
};
class InputAction : public Resource
{
GDCLASS(InputAction, Resource);
public:
void set_action_name(const StringName name) { this->action_name = name; }
StringName get_action_name() const { return this->action_name; }
void set_deadzone(const float zone) { this->deadzone = zone; }
float get_deadzone() const { return this->deadzone; }
void set_input_events(const TypedArray<InputEvent> events) { this->input_events = events; }
TypedArray<InputEvent> get_input_events() const { return this->input_events; }
protected:
StringName action_name = "action_name";
float deadzone = 0.5f;
TypedArray<InputEvent> input_events;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER(InputAction, action_name, Variant::STRING_NAME);
ADD_GETTER_SETTER(InputAction, deadzone, Variant::FLOAT);
ADD_GETTER_SETTER_HINTED(InputAction, input_events, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"));
}
};
class InputAxis : public Resource
{
GDCLASS(InputAxis, Resource);
public:
enum AXIS_DIMENSIONS
{
ONE,
TWO,
THREE
};
void set_axis_name(const StringName name) { this->axis_name = name; }
StringName get_axis_name() const { return this->axis_name; }
void set_axis_dimensions(const uint8_t dimensions) { this->axis_dimensions = (AXIS_DIMENSIONS)dimensions; }
uint8_t get_axis_dimensions() const { return (uint8_t)this->axis_dimensions; }
void set_deadzone(const float deadzone) { this->deadzone = deadzone; }
float get_deadzone() const { return this->deadzone; }
void set_include_mouse(const bool include) { this->include_mouse = include; }
bool get_include_mouse() const { return this->include_mouse; }
void set_left_right_events(const TypedArray<InputAxis2D> events) { this->left_right_events = events; }
TypedArray<InputAxis2D> get_left_right_events() const { return this->left_right_events; }
void set_forward_back_events(const TypedArray<InputAxis2D> events) { this->forward_back_events = events; }
TypedArray<InputAxis2D> get_forward_back_events() const { return this->forward_back_events; }
void set_up_down_events(const TypedArray<InputAxis2D> events) { this->up_down_events = events; }
TypedArray<InputAxis2D> get_up_down_events() const { return this->up_down_events; }
Vector3 get_spherical_vector() const;
Vector2 get_lateral_vector() const;
Vector2 get_lateral_vector_square() const;
float get_x_axis() const;
float get_y_axis() const;
float get_z_axis() const;
protected:
StringName axis_name = "axis_name";
AXIS_DIMENSIONS axis_dimensions = AXIS_DIMENSIONS::TWO;
float deadzone = 0.15f;
bool include_mouse = false;
TypedArray<InputAxis2D> left_right_events;
TypedArray<InputAxis2D> forward_back_events;
TypedArray<InputAxis2D> up_down_events;
// Godot boilerplate below
protected:
static void _bind_methods()
{
{ // action_name, axis_dimensions, deadzone, include_mouse
ADD_GETTER_SETTER(InputAxis, axis_name, Variant::STRING_NAME);
ADD_GETTER_SETTER_HINTED(InputAxis, axis_dimensions, Variant::INT, PROPERTY_HINT_ENUM, "One,Two,Three");
ADD_GETTER_SETTER(InputAxis, deadzone, Variant::FLOAT);
ADD_GETTER_SETTER(InputAxis, include_mouse, Variant::BOOL);
}
{ // left_right_events, forward_back_events, up_down_events
ADD_GETTER_SETTER_HINTED(InputAxis, left_right_events, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputAxis2D"));
ADD_GETTER_SETTER_HINTED(InputAxis, forward_back_events, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputAxis2D"));
ADD_GETTER_SETTER_HINTED(InputAxis, up_down_events, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputAxis2D"));
}
{ // Axis getters
BIND_METHOD(InputAxis, get_spherical_vector);
BIND_METHOD(InputAxis, get_lateral_vector);
BIND_METHOD(InputAxis, get_lateral_vector_square);
BIND_METHOD(InputAxis, get_x_axis);
BIND_METHOD(InputAxis, get_y_axis);
BIND_METHOD(InputAxis, get_z_axis);
}
{ // AXIS_DIMENSIONS enum constants
BIND_ENUM_CONSTANT(AXIS_DIMENSIONS::ONE);
BIND_ENUM_CONSTANT(AXIS_DIMENSIONS::TWO);
BIND_ENUM_CONSTANT(AXIS_DIMENSIONS::THREE);
}
}
};
VARIANT_ENUM_CAST(InputAxis::AXIS_DIMENSIONS);
class InputResource : public Resource
{
GDCLASS(InputResource, Resource);
public:
void update_buttons(const Ref<InputEvent> event, const float delta);
void update_axes(const float delta);
void update_axes_mouse_event(const Ref<InputEventMouseMotion> event, const float delta);
void set_input_actions(const TypedArray<InputAction> input_actions) { this->input_actions = input_actions; }
TypedArray<InputAction> get_input_actions() const { return this->input_actions; }
void set_input_axes(const TypedArray<InputAxis> input_axes) { this->input_axes = input_axes; }
TypedArray<InputAxis> get_input_axes() const { return this->input_axes; }
protected:
TypedArray<InputAction> input_actions;
TypedArray<InputAxis> input_axes;
// Godot boilerplate below
protected:
static void _bind_methods()
{
{ // input_actions, input_axes
ADD_GETTER_SETTER_HINTED(InputResource, input_actions, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputAction"));
ADD_GETTER_SETTER_HINTED(InputResource, input_axes, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "InputAxis"));
}
{ // update
BIND_METHOD_2PARAM(InputResource, update_buttons, event, delta);
BIND_METHOD_1PARAM(InputResource, update_axes, delta);
BIND_METHOD_2PARAM(InputResource, update_axes_mouse_event, event, delta);
}
}
};
@@ -0,0 +1,22 @@
/*
* ©2024 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 "resources/level_metadata_map.h"
Ref<LevelMetadataMapEntry> LevelMetadataMap::operator[](Ref<PackedScene> key)
{
for (uint32_t i = 0; i < this->entries.size(); i++)
{
const Ref<LevelMetadataMapEntry> entry = this->entries[i];
if(entry->get_scene() == key)
{
return entry;
}
}
return Ref<LevelMetadataMapEntry>();
}
@@ -0,0 +1,62 @@
/*
* ©2024 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 "level_metadata_resource.h"
#include <godot_cpp/classes/packed_scene.hpp>
using namespace godot;
//
class LevelMetadataMapEntry : public Resource
{
GDCLASS(LevelMetadataMapEntry, Resource);
public:
void set_scene(Ref<PackedScene> scene) { this->scene = scene; }
Ref<PackedScene> get_scene() const { return this->scene; }
void set_metadata(Ref<LevelMetadataResource> metadata) { this->metadata = metadata; }
Ref<LevelMetadataResource> get_metadata() const { return this->metadata; }
protected:
Ref<PackedScene> scene;
Ref<LevelMetadataResource> metadata;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(LevelMetadataMapEntry, scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
ADD_GETTER_SETTER_HINTED(LevelMetadataMapEntry, metadata, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "LevelMetadataResource");
}
};
//
class LevelMetadataMap : public Resource
{
GDCLASS(LevelMetadataMap, Resource);
public:
void set_entries(const TypedArray<LevelMetadataMapEntry> entries) { this->entries = entries; }
TypedArray<LevelMetadataMapEntry> get_entries() const { return this->entries; }
Ref<LevelMetadataMapEntry> operator[](Ref<PackedScene> key);
protected:
TypedArray<LevelMetadataMapEntry> entries;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(LevelMetadataMap, entries, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "LevelMetadataMapEntry"));
}
};
@@ -0,0 +1,11 @@
/*
* ©2024 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 "resources/level_metadata_resource.h"
@@ -0,0 +1,41 @@
/*
* ©2024 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 "orng_macros.h"
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/resource.hpp>
using namespace godot;
// A list of scenes to be used as the default in scenarios where it might not
// be obvious.
class LevelMetadataResource : public Resource
{
GDCLASS(LevelMetadataResource, Resource);
public:
void set_thumbnail(Ref<Image> thumbnail) { this->thumbnail = thumbnail; }
Ref<Image> get_thumbnail() const { return this->thumbnail; }
void set_level_name(const StringName name) { this->level_name = name; }
StringName get_level_name() const { return this->level_name; }
protected:
Ref<Image> thumbnail;
StringName level_name;
// Godot boilerplate below
protected:
static void _bind_methods()
{
ADD_GETTER_SETTER_HINTED(LevelMetadataResource, thumbnail, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Image");
ADD_GETTER_SETTER(LevelMetadataResource, level_name, Variant::STRING_NAME);
}
};
+4
View File
@@ -0,0 +1,4 @@
[ViewState]
Mode=
Vid=
FolderType=Generic
+264
View File
@@ -0,0 +1,264 @@
/*
* ©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 "input_handler.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
#include <godot_cpp/classes/input_map.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
GDSINGLETON_CPP(InputHandler);
void InputHandler::_process(double delta)
{
for (InputResource *resource : this->active_resources)
{
resource->update_axes(delta);
}
}
void InputHandler::_input(const Ref<InputEvent> &event)
{
for (InputResource *resource : this->active_resources)
{
if (event->is_action_type())
{
resource->update_buttons(event, this->get_process_delta_time());
}
else if (const InputEventMouseMotion *mouse_event = cast_to<InputEventMouseMotion>(event.ptr()))
{
const Input *input = Input::get_singleton();
if (input->get_mouse_mode() != Input::MOUSE_MODE_VISIBLE)
{
resource->update_axes_mouse_event(event, this->get_process_delta_time());
}
}
}
}
void InputHandler::add_input_resource(Node *target, InputResource *resource)
{
InputMap *input_map = InputMap::get_singleton();
const TypedArray<InputAction> &input_actions = resource->get_input_actions();
const TypedArray<InputAxis> &input_axes = resource->get_input_axes();
for(uint8_t i = 0; i < input_actions.size(); i++)
{
if (const InputAction *action = cast_to<InputAction>(input_actions[i]))
{
if (!input_map->has_action(action->get_action_name()))
{
input_map->add_action(action->get_action_name(), action->get_deadzone());
}
for(uint8_t s = 0; s < 2; s++)
{
const StringName signal_name = StringName("_on_") + action->get_action_name() + this->input_action_suffixes[s];
if (target->has_method(signal_name))
{
const TypedArray<InputEvent> &events = action->get_input_events();
if (!resource->has_signal(signal_name))
{
resource->add_user_signal(signal_name);
}
for (uint8_t e = 0; e < events.size(); e++)
{
const Ref<InputEvent> &event = events[e];
input_map->action_add_event(action->get_action_name(), event);
}
resource->connect(signal_name, Callable(target, signal_name));
}
}
}
}
for(uint8_t i = 0; i < input_axes.size(); i++)
{
if (const InputAxis *axis = cast_to<InputAxis>(input_axes[i]))
{
const uint8_t &axis_dimensions = axis->get_axis_dimensions();
for(uint8_t s = 0; s <= axis_dimensions; s++)
{
const StringName signal_name = StringName("_on_") + axis->get_axis_name() + this->input_axis_suffixes[s];
if (!resource->has_signal(signal_name))
{
resource->add_user_signal(signal_name);
}
if (target->has_method(signal_name))
{
resource->connect(signal_name, Callable(target, signal_name));
}
}
const StringName &axis_name = axis->get_axis_name();
const float &deadzone = axis->get_deadzone();
if (axis_dimensions >= InputAxis::AXIS_DIMENSIONS::ONE)
{
const StringName &left_axis_action = axis_name + INPUTAXIS_LEFT_SUFFIX;
const StringName &right_axis_action = axis_name + INPUTAXIS_RIGHT_SUFFIX;
if (!input_map->has_action(left_axis_action)) input_map->add_action(left_axis_action, deadzone);
if (!input_map->has_action(right_axis_action)) input_map->add_action(right_axis_action, deadzone);
const TypedArray<InputAxis2D> &left_right_events = axis->get_left_right_events();
for(uint8_t e = 0; e < left_right_events.size(); e++)
{
const Ref<InputAxis2D> &left_right_event = left_right_events[e];
input_map->action_add_event(left_axis_action, left_right_event->get_negative());
input_map->action_add_event(right_axis_action, left_right_event->get_positive());
}
}
if (axis_dimensions >= InputAxis::AXIS_DIMENSIONS::TWO)
{
const StringName &back_axis_action = axis_name + INPUTAXIS_BACK_SUFFIX;
const StringName &forward_axis_action = axis_name + INPUTAXIS_FORWARD_SUFFIX;
if (!input_map->has_action(back_axis_action)) input_map->add_action(back_axis_action, deadzone);
if (!input_map->has_action(forward_axis_action)) input_map->add_action(forward_axis_action, deadzone);
const TypedArray<InputAxis2D> &forward_back_events = axis->get_forward_back_events();
for(uint8_t e = 0; e < forward_back_events.size(); e++)
{
const Ref<InputAxis2D> &forward_back_event = forward_back_events[e];
input_map->action_add_event(back_axis_action, forward_back_event->get_negative());
input_map->action_add_event(forward_axis_action, forward_back_event->get_positive());
}
}
if (axis_dimensions >= InputAxis::AXIS_DIMENSIONS::THREE)
{
const StringName &down_axis_action = axis_name + INPUTAXIS_DOWN_SUFFIX;
const StringName &up_axis_action = axis_name + INPUTAXIS_UP_SUFFIX;
if (!input_map->has_action(down_axis_action)) input_map->add_action(down_axis_action, deadzone);
if (!input_map->has_action(up_axis_action)) input_map->add_action(up_axis_action, deadzone);
const TypedArray<InputAxis2D> &up_down_events = axis->get_up_down_events();
for(uint8_t e = 0; e < up_down_events.size(); e++)
{
const Ref<InputAxis2D> &up_down_event = up_down_events[e];
input_map->action_add_event(down_axis_action, up_down_event->get_negative());
input_map->action_add_event(up_axis_action, up_down_event->get_positive());
}
}
if (axis->get_include_mouse())
{
Engine *engine = Engine::get_singleton();
this->call_deferred("_enable_mouse_capture");
}
}
}
this->active_resources.insert(resource);
const StringName input_resource_method = target->get_name() + StringName("_") + resource->get_path();
if (this->bound_remove_methods.find(input_resource_method) == this->bound_remove_methods.end())
{
MethodBind *bind = ClassDB::bind_method(input_resource_method, &InputHandler::remove_input_resource);
this->bound_remove_methods.emplace(input_resource_method, bind);
}
target->connect("tree_exiting", Callable(this, input_resource_method).bind(target, resource), CONNECT_ONE_SHOT);
}
void InputHandler::_enable_mouse_capture()
{
Input *input = Input::get_singleton();
input->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
}
void InputHandler::remove_input_resource(Node *target, InputResource *resource)
{
InputMap *input_map = InputMap::get_singleton();
const TypedArray<InputAction> &input_actions = resource->get_input_actions();
const TypedArray<InputAxis> &input_axes = resource->get_input_axes();
for(uint8_t i = 0; i < input_actions.size(); i++)
{
if (const InputAction *action = cast_to<InputAction>(input_actions[i]))
{
input_map->action_erase_events(action->get_action_name());
for(uint8_t s = 0; s < 2; s++)
{
const StringName signal_name = StringName("_on_") + action->get_action_name() + input_action_suffixes[s];
if (target->has_method(signal_name))
{
resource->disconnect(signal_name, Callable(target, signal_name));
}
}
}
}
for(uint8_t i = 0; i < input_axes.size(); i++)
{
if (const InputAxis *axis = cast_to<InputAxis>(input_axes[i]))
{
for(uint8_t s = 0; s < 3; s++)
{
const StringName signal_name = StringName("_on_") + axis->get_axis_name() + this->input_axis_suffixes[s];
if (target->has_method(signal_name))
{
resource->disconnect(signal_name, Callable(target, signal_name));
}
}
/*
* This part would be a good, safe precaution to take, but it seems
* to cause inputs to stick if an input resource is removed while
* an axis is being used. Removing this seems to have no negative
* side effects, but I'm keeping it commented here in case there
* turns out to be a reason to put it back later.
*/
// const StringName &axis_name = axis->get_axis_name();
// const uint8_t &axis_dimensions = axis->get_axis_dimensions();
// const float &deadzone = axis->get_deadzone();
// if (axis_dimensions >= InputAxis::AXIS_DIMENSIONS::ONE)
// {
// const StringName &left_axis_action = axis_name + INPUTAXIS_LEFT_SUFFIX;
// const StringName &right_axis_action = axis_name + INPUTAXIS_RIGHT_SUFFIX;
// input_map->action_erase_events(left_axis_action);
// input_map->action_erase_events(right_axis_action);
// }
// if (axis_dimensions >= InputAxis::AXIS_DIMENSIONS::TWO)
// {
// const StringName &back_axis_action = axis_name + INPUTAXIS_BACK_SUFFIX;
// const StringName &forward_axis_action = axis_name + INPUTAXIS_FORWARD_SUFFIX;
// input_map->action_erase_events(back_axis_action);
// input_map->action_erase_events(forward_axis_action);
// }
// if (axis_dimensions >= InputAxis::AXIS_DIMENSIONS::THREE)
// {
// const StringName &down_axis_action = axis_name + INPUTAXIS_DOWN_SUFFIX;
// const StringName &up_axis_action = axis_name + INPUTAXIS_UP_SUFFIX;
// input_map->action_erase_events(down_axis_action);
// input_map->action_erase_events(up_axis_action);
// }
if (axis->get_include_mouse())
{
Engine *engine = Engine::get_singleton();
this->call_deferred("_disable_mouse_capture");
}
}
}
this->active_resources.erase(resource);
}
void InputHandler::_disable_mouse_capture()
{
Input *input = Input::get_singleton();
input->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
}
+57
View File
@@ -0,0 +1,57 @@
/*
* ©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 "orng_macros.h"
#include "singletons/singleton_interface.h"
#include <map>
#include <unordered_set>
#include "resources/input_resource.h"
class InputHandler : public Node, SingletonInterface
{
GDCLASS(InputHandler, Node);
GDSINGLETON(InputHandler);
public:
virtual void _process(double delta) override;
virtual void _input(const Ref<InputEvent> &event) override;
void add_input_resource(Node *target, InputResource *resource);
void remove_input_resource(Node *target, InputResource *resource);
protected:
std::unordered_set<InputResource*> active_resources;
private:
void _enable_mouse_capture();
void _disable_mouse_capture();
const StringName input_action_suffixes[2] = { INPUTACTION_SUFFIX_PRESSED, INPUTACTION_SUFFIX_RELEASED };
const StringName input_axis_suffixes[3] = { INPUTAXIS_ONE_DIMENSIONAL_SUFFIX, INPUTAXIS_TWO_DIMENSIONAL_SUFFIX, INPUTAXIS_THREE_DIMENSIONAL_SUFFIX };
std::map<const StringName, MethodBind*> bound_remove_methods;
// Godot boilerplate below
protected:
static void _bind_methods()
{
GDSINGLETON_BIND(InputHandler);
{
BIND_METHOD_2PARAM(InputHandler, add_input_resource, target, resource);
BIND_METHOD_2PARAM(InputHandler, remove_input_resource, target, resource);
BIND_METHOD(InputHandler, _enable_mouse_capture);
BIND_METHOD(InputHandler, _disable_mouse_capture);
}
}
};
+168
View File
@@ -0,0 +1,168 @@
/*
* ©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_manager.h"
#include "nodes/player_spawn.h"
#include <godot_cpp/classes/dir_access.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/file_access.hpp>
#include <godot_cpp/classes/json.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
GDSINGLETON_CPP(SaveManager);
SaveManager::SaveManager()
{
this->save_file_data = memnew(SaveFileData);
this->save_file_index = memnew(SaveFileIndex);
this->read_save_index_data();
}
SaveManager::~SaveManager()
{
if (this->save_file_index) memdelete(this->save_file_index);
if (this->save_file_data) memdelete(this->save_file_data);
}
void SaveManager::set_new_level(StringName level)
{
this->save_file_data->level = level;
this->set_new_checkpoint(PLAYER_ORIGIN_TAG);
}
void SaveManager::set_new_checkpoint(StringName checkpoint_name)
{
if(this->save_file_data->checkpoint != checkpoint_name)
{
this->save_file_data->checkpoint = checkpoint_name;
this->write_save_file_data();
}
}
void SaveManager::create_new_save_file_data(uint8_t slot, SaveFileData::DifficultySetting difficulty)
{
if (this->save_file_data)
{
memdelete(this->save_file_data);
}
this->save_file_data = memnew(SaveFileData);
this->save_file_data->init();
this->current_slot = slot;
this->current_difficulty = difficulty;
this->save_file_index->clear_slot(slot);
}
void SaveManager::read_save_file_data(uint8_t slot)
{
#ifdef DEBUG_ENABLED
Ref<FileAccess> save_file = FileAccess::open(SaveManager::build_slot_name(slot) + StringName(SAVE_FILE_DEBUG_SUFFIX), FileAccess::READ);
#else
Ref<FileAccess> save_file = FileAccess::open_compressed(SaveManager::build_slot_name(slot), FileAccess::READ, FileAccess::COMPRESSION_GZIP);
#endif
if(save_file.is_valid())
{
#ifdef DEBUG_ENABLED
const Dictionary &dictionary = JSON::parse_string(save_file->get_as_text());
#else
const Dictionary &dictionary = save_file->get_var();
#endif
this->save_file_data->deserialize(dictionary);
}
this->read_save_index_data();
}
void SaveManager::read_save_index_data()
{
#ifdef DEBUG_ENABLED
Ref<FileAccess> save_index = FileAccess::open(SaveManager::build_index_name() + StringName(SAVE_FILE_DEBUG_SUFFIX), FileAccess::READ);
#else
Ref<FileAccess> save_index = FileAccess::open_compressed(SaveManager::build_index_name(), FileAccess::READ, FileAccess::COMPRESSION_GZIP);
#endif
if(save_index.is_valid())
{
#ifdef DEBUG_ENABLED
const Dictionary &dictionary = JSON::parse_string(save_index->get_as_text());
#else
const Dictionary &dictionary = save_index->get_var();
#endif
this->save_file_index->deserialize(dictionary);
}
}
void SaveManager::write_save_file_data()
{
const Dictionary &dictionary = this->save_file_data->serialize();
DirAccess::make_dir_absolute(SAVE_FOLDER);
#ifdef DEBUG_ENABLED
Ref<FileAccess> save_file = FileAccess::open(SaveManager::build_slot_name(this->current_slot) + StringName(SAVE_FILE_DEBUG_SUFFIX), FileAccess::WRITE);
#else
Ref<FileAccess> save_file = FileAccess::open_compressed(SaveManager::build_slot_name(this->current_slot), FileAccess::WRITE, FileAccess::COMPRESSION_GZIP);
#endif
if(save_file.is_valid())
{
#ifdef DEBUG_ENABLED
save_file->store_string(JSON::stringify(dictionary));
#else
save_file->store_var(dictionary);
#endif
save_file->close();
}
this->save_file_index->update_header(this->current_slot, *this->save_file_data);
this->write_save_index_data();
}
void SaveManager::write_save_index_data()
{
const Dictionary &dictionary = this->save_file_index->serialize();
DirAccess::make_dir_absolute(SAVE_FOLDER);
#ifdef DEBUG_ENABLED
Ref<FileAccess> save_index = FileAccess::open(SaveManager::build_index_name() + StringName(SAVE_FILE_DEBUG_SUFFIX), FileAccess::WRITE);
#else
Ref<FileAccess> save_index = FileAccess::open_compressed(SaveManager::build_index_name(), FileAccess::WRITE, FileAccess::COMPRESSION_GZIP);
#endif
if(save_index.is_valid())
{
#ifdef DEBUG_ENABLED
save_index->store_string(JSON::stringify(dictionary));
#else
save_index->store_var(dictionary);
#endif
save_index->close();
}
}
void SaveManager::delete_save_file_data(uint8_t slot)
{
DirAccess::remove_absolute(SaveManager::build_slot_name(slot));
this->save_file_index->clear_slot(slot);
}
bool SaveManager::does_save_file_exist(uint8_t slot) const
{
return FileAccess::file_exists(SaveManager::build_slot_name(slot));
}
bool SaveManager::do_any_save_files_exist() const
{
for (std::pair<const uint8_t, std::unique_ptr<SaveHeaderData>> &header : this->save_file_index->headers)
{
if (FileAccess::file_exists(SaveManager::build_slot_name(header.first)))
{
return true;
}
}
return false;
}
+89
View File
@@ -0,0 +1,89 @@
/*
* ©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 "singleton_interface.h"
#include "orng_macros.h"
#include "data_assets/save_file_data.h"
#include <godot_cpp/classes/node.hpp>
using namespace godot;
#define SAVE_FOLDER "user://save_data/"
#define SAVE_FILE_PREFIX "save_file_"
#define SAVE_HEADER_INDEX_NAME "save_index"
#define SAVE_FILE_EXTENSION ".sav"
#define SAVE_FILE_DEBUG_SUFFIX "-debug"
class SaveManager : public Node, public SingletonInterface
{
GDCLASS(SaveManager, Node);
GDSINGLETON(SaveManager);
public:
SaveManager();
~SaveManager();
void create_new_save_file_data(uint8_t slot, SaveFileData::DifficultySetting difficulty);
void read_save_file_data(uint8_t slot);
void read_save_index_data();
void write_save_file_data();
void write_save_index_data();
void delete_save_file_data(uint8_t slot);
bool does_save_file_exist(uint8_t slot) const;
bool do_any_save_files_exist() const;
SaveHeaderData *get_save_file_header(uint8_t slot) const { return this->save_file_index->headers[slot].get(); }
SaveFileData *get_save_file_data() const { return this->save_file_data; }
void set_new_level(StringName level);
void set_new_checkpoint(StringName checkpoint_name);
StringName get_current_level() const { return this->save_file_data ? this->save_file_data->level : ""; }
StringName get_current_checkpoint() const { return this->save_file_data ? this->save_file_data->checkpoint : ""; }
static const StringName build_slot_name(const uint8_t slot_number) { return StringName(SAVE_FOLDER) + StringName(SAVE_FILE_PREFIX) + itos(slot_number) + StringName(SAVE_FILE_EXTENSION); }
static const StringName build_index_name() { return StringName(SAVE_FOLDER) + StringName(SAVE_HEADER_INDEX_NAME) + StringName(SAVE_FILE_EXTENSION); }
private:
SaveFileData *save_file_data = nullptr;
SaveFileIndex *save_file_index = nullptr;
uint8_t current_slot = 0;
SaveFileData::DifficultySetting current_difficulty = SaveFileData::DifficultySetting::NORMAL;
int32_t user_index = 0;
// Godot boilerplate below
protected:
static void _bind_methods()
{
{
GDSINGLETON_BIND(SaveManager);
{
BIND_METHOD_2PARAM(SaveManager, create_new_save_file_data, slot, difficulty);
BIND_METHOD_1PARAM(SaveManager, read_save_file_data, slot);
BIND_METHOD(SaveManager, write_save_file_data);
BIND_METHOD_1PARAM(SaveManager, delete_save_file_data, slot);
BIND_METHOD_1PARAM(SaveManager, does_save_file_exist, slot);
BIND_METHOD(SaveManager, do_any_save_files_exist);
BIND_METHOD(SaveManager, get_current_level);
BIND_METHOD(SaveManager, get_current_checkpoint);
ADD_SIGNAL(MethodInfo("save_file_loaded", PropertyInfo(Variant::INT, "slot"), PropertyInfo(Variant::OBJECT, "save_header", PROPERTY_HINT_RESOURCE_TYPE, "SaveHeaderData"), PropertyInfo(Variant::BOOL, "success")));
ADD_SIGNAL(MethodInfo("save_file_saved", PropertyInfo(Variant::INT, "slot"), PropertyInfo(Variant::OBJECT, "save_header", PROPERTY_HINT_RESOURCE_TYPE, "SaveHeaderData"), PropertyInfo(Variant::BOOL, "success")));
}
}
}
};
+220
View File
@@ -0,0 +1,220 @@
/*
* ©2024 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 "scene_loader.h"
#include "orng_macros.h"
#include "singletons/save_manager.h"
#include "singletons/vendor_service/vendor_service.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/scene_tree_timer.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
GDSINGLETON_CPP(SceneLoader)
SceneLoader::SceneLoader()
{
this->enable_load_screen = true;
}
void SceneLoader::_enter_tree()
{
this->resource_loader = ResourceLoader::get_singleton();
Ref<PackedScene> loading_screen = this->resource_loader->load("uid://d0d7k8ebwixgp");
this->_loading_screen_instance = loading_screen->instantiate();
this->_loading_screen_instance->connect("load_ready", callable_mp(this, &SceneLoader::_loading_screen_initialised));
this->_loading_screen_instance->connect("beginning_fade_out", callable_mp(this, &SceneLoader::_finish_loading_next_level));
this->_loading_screen_instance->connect("load_finished", callable_mp(this, &SceneLoader::_post_load_cleanup));
}
void SceneLoader::load_level(const StringName path)
{
this->enable_load_screen = true;
this->_continue_load_level(path);
}
void SceneLoader::load_level_without_load_screen(const StringName path)
{
this->enable_load_screen = false;
this->_continue_load_level(path);
}
void SceneLoader::load_save_file_level()
{
this->enable_load_screen = true;
this->_continue_load_level(SaveManager::get_singleton()->get_current_level());
}
void SceneLoader::_continue_load_level(const StringName path)
{
this->_level_to_load = path;
if (this->enable_load_screen)
this->_initialise_loading_screen();
else
this->_loading_screen_initialised();
}
void SceneLoader::_initialise_loading_screen()
{
if (!this->_loading_screen_instance->is_inside_tree())
{
this->get_tree()->get_root()->add_child(this->_loading_screen_instance);
this->_loading_screen_instance->call("begin_fade_in");
}
}
void SceneLoader::_loading_screen_initialised()
{
Node *current_level = this->get_tree()->get_current_scene();
if (current_level)
{
current_level->connect("tree_exited", callable_mp(this, &SceneLoader::_previous_level_deleted), ConnectFlags::CONNECT_ONE_SHOT);
current_level->queue_free();
current_level = nullptr;
}
else
{
this->_previous_level_deleted();
}
}
void SceneLoader::_previous_level_deleted()
{
if (this->resource_loader->load_threaded_request(this->_level_to_load, "PackedScene", true) != Error::OK)
{
UtilityFunctions::printerr("Scene ", this->_level_to_load, " does not exist.");
return;
}
CALL_NEXT_FRAME(callable_mp(this, &SceneLoader::_resource_load_recursive));
}
void SceneLoader::_resource_load_recursive()
{
ResourceLoader::ThreadLoadStatus load_status = this->resource_loader->load_threaded_get_status(this->_level_to_load);
switch (load_status)
{
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_LOADED:
if (this->enable_load_screen)
CALL_NEXT_FRAME(callable_mp(this, &SceneLoader::_begin_load_screen_fade_out));
else
CALL_NEXT_FRAME(callable_mp(this, &SceneLoader::_finish_loading_next_level));
return;
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_INVALID_RESOURCE:
UtilityFunctions::printerr(this->_level_to_load, " is not a valid resource.");
return;
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_FAILED:
UtilityFunctions::printerr("Can not load ", this->_level_to_load, " for an unknown reason.");
return;
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_IN_PROGRESS:
break;
}
CALL_NEXT_FRAME(callable_mp(this, &SceneLoader::_resource_load_recursive));
}
void SceneLoader::_begin_load_screen_fade_out()
{
if (this->enable_load_screen)
this->_loading_screen_instance->call("begin_fade_out");
}
void SceneLoader::_finish_loading_next_level()
{
VendorService *vendor_service = VendorService::get_singleton();
if (!vendor_service->is_game_ownership_valid())
{
if (this->enable_load_screen && UtilityFunctions::randf() < 0.2f)
{
return;
}
}
Ref<PackedScene> next_scene_resource = this->resource_loader->load_threaded_get(this->_level_to_load);
if (next_scene_resource.is_valid())
{
this->get_tree()->change_scene_to_packed(next_scene_resource);
}
}
void SceneLoader::_post_load_cleanup()
{
this->get_tree()->get_root()->remove_child(this->_loading_screen_instance);
}
void SceneLoader::load_scene(const StringName path, Callable callback)
{
if (path.is_empty())
return;
if (this->resource_loader->load_threaded_request(path, "PackedScene", true) != Error::OK)
{
UtilityFunctions::printerr("Scene ", path, " does not exist.");
return;
}
this->_scenes_to_load.emplace(path, callback);
if (this->_scenes_loading == false)
{
this->_scenes_loading = true;
CALL_NEXT_FRAME(callable_mp(this, &SceneLoader::_resource_load_scene_recursive));
}
}
void SceneLoader::_resource_load_scene_recursive()
{
std::vector<std::pair<StringName, Callable>> finished_scenes;
for (const std::pair<StringName, Callable> &scene : this->_scenes_to_load)
{
ResourceLoader::ThreadLoadStatus load_status = this->resource_loader->load_threaded_get_status(scene.first);
switch (load_status)
{
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_LOADED:
finished_scenes.push_back(scene);
this->_scenes_to_load.erase(scene.first);
break;
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_INVALID_RESOURCE:
UtilityFunctions::printerr(scene.first, " is not a valid resource.");
return;
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_FAILED:
UtilityFunctions::printerr("Can not load ", scene.first, " for an unknown reason.");
return;
case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_IN_PROGRESS:
break;
}
}
for (const std::pair<StringName, Callable> &scene : finished_scenes)
{
Ref<PackedScene> next_scene_resource = this->resource_loader->load_threaded_get(scene.first);
scene.second.call_deferred(next_scene_resource);
}
if (this->_scenes_to_load.empty())
this->_scenes_loading = false;
else
CALL_NEXT_FRAME(callable_mp(this, &SceneLoader::_resource_load_scene_recursive));
}
+75
View File
@@ -0,0 +1,75 @@
/*
* ©2024 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 "singleton_interface.h"
#include "orng_macros.h"
#include <map>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
using namespace godot;
class SceneLoader : public Node, public SingletonInterface
{
GDCLASS(SceneLoader, Node);
GDSINGLETON(SceneLoader);
public:
SceneLoader();
virtual void _enter_tree() override;
void load_level(const StringName path);
void load_level_without_load_screen(const StringName path);
void load_save_file_level();
void load_scene(const StringName path, Callable callback);
private:
void _continue_load_level(const StringName path);
void _initialise_loading_screen();
void _loading_screen_initialised();
void _previous_level_deleted();
void _resource_load_recursive();
void _begin_load_screen_fade_out();
void _finish_loading_next_level();
void _post_load_cleanup();
void _resource_load_scene_recursive();
uint8_t enable_load_screen : 1;
ResourceLoader *resource_loader = nullptr;
Node *_loading_screen_instance = nullptr;
StringName _level_to_load;
std::map<StringName, Callable> _scenes_to_load;
bool _scenes_loading = false;
// Godot boilerplate below
protected:
static void _bind_methods()
{
GDSINGLETON_BIND(SceneLoader);
// public:
BIND_METHOD_1PARAM(SceneLoader, load_level, path);
BIND_METHOD_1PARAM(SceneLoader, load_level_without_load_screen, path);
BIND_METHOD(SceneLoader, load_save_file_level);
// private:
BIND_METHOD(SceneLoader, _loading_screen_initialised);
BIND_METHOD(SceneLoader, _resource_load_recursive);
BIND_METHOD(SceneLoader, _begin_load_screen_fade_out);
BIND_METHOD(SceneLoader, _finish_loading_next_level);
BIND_METHOD(SceneLoader, _post_load_cleanup);
BIND_METHOD(SceneLoader, _resource_load_scene_recursive);
}
};
@@ -0,0 +1,98 @@
/*
* ©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.
*/
#ifndef _SINGLETON_INTERFACE_H__
#define _SINGLETON_INTERFACE_H__
#include <godot_cpp/classes/node.hpp>
using namespace godot;
/**
* Define the global pointer used to store the singleton instance used by the
* module functions. Place this below `using namespace godot;` in the
* register_types.cpp file.
*/
#define GDSINGLETON_REGISTER_PTR(Class, Pointer) Class *Pointer = nullptr
/**
* Register the singleton with the engine and perform initialisation. Place
* this inside the `initialize_*_module()` function.
*/
#define GDSINGLETON_REGISTER_CLASS(Class, Pointer) \
ClassDB::register_class<Class>(); \
Pointer = memnew(Class); Pointer->init();
/**
* Unregister the singleton with the engine and perform uninitialisation. Place
* this inside the `uninitialize_*_module()` function.
*/
#define GDSINGLETON_UNREGISTER_CLASS(Pointer) \
if (UtilityFunctions::is_instance_valid(Pointer)) { \
Pointer->uninit(); \
memdelete(Pointer); Pointer = nullptr; }
/**
* Used in classes that derive from SingletonInterface. Place just below the
* GDCLASS() macro in the header definition, and ensure the class derives
* from SingletonInterface.
*/
#define GDSINGLETON(Class) \
private: \
virtual void _finish_init() override; \
protected: \
static Class *singleton; \
public: \
virtual void init() override; \
virtual void uninit() override; \
static Class \
*get_singleton() \
{ return singleton; } \
/**
* Used in the class CPP file to set up the init and uninit functions and
* handle registration with the engine. Place below `using namespace godot;`.
*/
#define GDSINGLETON_CPP(Class) \
Class *Class::singleton = nullptr; \
void Class::init() { \
if (!Class::singleton) { \
Class::singleton = this; \
Engine *engine = Engine::get_singleton(); \
if(engine->has_singleton(#Class)) { \
engine->unregister_singleton(#Class); } \
engine->register_singleton(#Class, this); \
this->call_deferred("_finish_init"); }} \
void Class::_finish_init() { \
Engine *engine = Engine::get_singleton(); \
Window *tree_root = Object::cast_to<SceneTree>( \
engine->get_main_loop())->get_root(); \
tree_root->add_child(this); } \
void Class::uninit() { \
singleton = nullptr; \
Engine *engine = Engine::get_singleton(); \
if (engine->has_singleton(#Class)) { \
engine->unregister_singleton(#Class); } \
}
/**
* Used to bind methods used by SingletonInterface to call deferred init code.
* Place this in the _bind_methods() function.
*/
#define GDSINGLETON_BIND(Class) ClassDB::bind_method(D_METHOD("_finish_init"), &Class::_finish_init)
class SingletonInterface
{
public:
virtual void init() = 0;
virtual void uninit() = 0;
private:
virtual void _finish_init() = 0;
};
#endif // _SINGLETON_INTERFACE_H__
@@ -0,0 +1,59 @@
/*
* ©2024 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 "singletons/vendor_service/vendor_service.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/scene_tree_timer.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
GDSINGLETON_CPP(VendorService);
void VendorService::_enter_tree()
{
Engine *engine = Engine::get_singleton();
#ifdef DEBUG_ENABLED
if (!engine->is_editor_hint())
{
#endif
if (this->steam_service = engine->get_singleton("Steam"))
{
const int steam_appid = STEAM_APP_ID;
this->steam_service->call("steamInitEx", false, steam_appid);
this->userid = this->steam_service->call("getSteamID");
this->username = this->steam_service->call("getPersonaName");
}
#ifdef DEBUG_ENABLED
}
#endif
}
bool VendorService::is_game_ownership_valid()
{
if (this->steam_service)
{
int ownership_proof = 0;
ownership_proof += this->steam_service->call("isSubscribed") ? 1 : 0;
ownership_proof += this->steam_service->call("isSubscribedFromFamilySharing") ? 1 : 0;
ownership_proof += this->steam_service->call("isSubscribedFromFreeWeekend") ? 1 : 0;
Dictionary timed_trial_values = this->steam_service->call("isTimedTrial");
if (!timed_trial_values.is_empty())
ownership_proof += (uint32_t)UtilityFunctions::clamp(uint32_t(timed_trial_values["seconds_allowed"]) - uint32_t(timed_trial_values["seconds_played"]), 0, UINT32_MAX);
return ownership_proof;
}
return false;
}
@@ -0,0 +1,58 @@
/*
* ©2024 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 "orng_macros.h"
#include "singletons/singleton_interface.h"
#ifdef DEBUG_ENABLED
#define STEAM_APP_ID ProjectSettings::get_singleton()->get_setting_with_override("orange_cat/vendor_service/steam/app_id")
#else
#define STEAM_APP_ID 480
#endif
class VendorService : public Node, public SingletonInterface
{
GDCLASS(VendorService, Node);
GDSINGLETON(VendorService);
public:
virtual void _enter_tree() override;
virtual bool is_game_ownership_valid();
private:
Object *steam_service = nullptr;
int userid = 0;
StringName username;
// Godot boilerplate below
protected:
static void _bind_methods()
{
GDSINGLETON_BIND(VendorService);
{
// BIND_METHOD_2PARAM(SaveManager, create_new_save_file_data, slot, difficulty);
// BIND_METHOD_1PARAM(SaveManager, read_save_file_data, slot);
// BIND_METHOD(SaveManager, write_save_file_data);
// BIND_METHOD_1PARAM(SaveManager, delete_save_file_data, slot);
// BIND_METHOD_1PARAM(SaveManager, does_save_file_exist, slot);
// BIND_METHOD(SaveManager, do_any_save_files_exist);
// BIND_METHOD(SaveManager, get_current_level);
// BIND_METHOD(SaveManager, get_current_checkpoint);
// ADD_SIGNAL(MethodInfo("save_file_loaded", PropertyInfo(Variant::INT, "slot"), PropertyInfo(Variant::OBJECT, "save_header", PROPERTY_HINT_RESOURCE_TYPE, "SaveHeaderData"), PropertyInfo(Variant::BOOL, "success")));
// ADD_SIGNAL(MethodInfo("save_file_saved", PropertyInfo(Variant::INT, "slot"), PropertyInfo(Variant::OBJECT, "save_header", PROPERTY_HINT_RESOURCE_TYPE, "SaveHeaderData"), PropertyInfo(Variant::BOOL, "success")));
}
}
};