diff --git a/godot/addons/monads/option.gd b/godot/addons/monads/option.gd index 6b5815a..327f46c 100644 --- a/godot/addons/monads/option.gd +++ b/godot/addons/monads/option.gd @@ -27,9 +27,15 @@ class Some extends Option: func _init(val: Variant) -> void: value = val - func chain(fn: Callable) -> Option: + func and_then(fn: Callable) -> Option: return fn.call(value) + func and_other(other: Option) -> Option: + return other + + func contains(target_value: Variant) -> bool: + return value == target_value + func filter(fn: Callable) -> Option: if fn.call(value): return self else: return Option.none @@ -42,25 +48,50 @@ class Some extends Option: func map(fn: Callable) -> Option: return Option.some(fn.call(value)) + func ok_or(_err: Variant) -> Result: + return Result.ok(value) + + func ok_or_else(fn: Callable) -> Result: + return Result.ok(value) + + func or_other(option: Option) -> Option: + return self + + func inspect(fn: Callable) -> Option: + fn.call(value) + return self + func is_some() -> bool: return true func is_none() -> bool: return false - func or_default(_default_value: Variant) -> Variant: - return value - - func or_else(_fn: Callable) -> Variant: - return value - func unwrap() -> Variant: return value + func unwrap_or(_default_value: Variant) -> Variant: + return value + + func unwrap_or_else(_fn: Callable) -> Variant: + return value + + func xor_other(other: Option) -> Option: + if other.is_none(): + return self + else: + return Option.none + class None extends Option: - func chain(_fn: Callable) -> Option: + func and_then(_fn: Callable) -> Option: return self + func and_other(other: Option) -> Option: + return self + + func contains(value: Variant) -> bool: + return false + func filter(_f: Callable) -> Option: return self @@ -70,18 +101,36 @@ class None extends Option: func map(_fn: Callable) -> Option: return self + func ok_or(err: Variant) -> Result: + return Result.err(err) + + func ok_or_else(fn: Callable) -> Result: + return Result.err(fn.call()) + + func or_other(option: Option) -> Option: + return option + + func inspect(_fn: Callable) -> Option: + return self + func is_some() -> bool: return false func is_none() -> bool: return true - func or_default(default_value: Variant) -> Variant: - return default_value - - func or_else(fn: Callable) -> Variant: - return fn.call() - func unwrap() -> Variant: assert(false, "Called unwrap() on a None value") return null + + func unwrap_or(default_value: Variant) -> Variant: + return default_value + + func unwrap_or_else(fn: Callable) -> Variant: + return fn.call() + + func xor_other(other: Option) -> Option: + if other.is_some(): + return other + else: + return self diff --git a/godot/assets/mann.glb b/godot/assets/mann.glb new file mode 100644 index 0000000..af341e7 Binary files /dev/null and b/godot/assets/mann.glb differ diff --git a/godot/assets/mann.glb.import b/godot/assets/mann.glb.import new file mode 100644 index 0000000..91d309f --- /dev/null +++ b/godot/assets/mann.glb.import @@ -0,0 +1,44 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://cyrwuqefk3efr" +path="res://.godot/imported/mann.glb-288e378e28910734d9c899c96305cd64.scn" + +[deps] + +source_file="res://assets/mann.glb" +dest_files=["res://.godot/imported/mann.glb-288e378e28910734d9c899c96305cd64.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/apply_root_scale=true +nodes/root_scale=0.008 +nodes/import_as_skeleton_bones=false +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="" +_subresources={ +"nodes": { +"PATH:AnimationPlayer": { +"import_tracks/position": 0, +"import_tracks/scale": 2 +} +} +} +gltf/naming_version=1 +gltf/embedded_image_handling=1 diff --git a/godot/assets/mann_0.png b/godot/assets/mann_0.png new file mode 100644 index 0000000..698a371 Binary files /dev/null and b/godot/assets/mann_0.png differ diff --git a/godot/assets/mann_0.png.import b/godot/assets/mann_0.png.import new file mode 100644 index 0000000..7766f81 --- /dev/null +++ b/godot/assets/mann_0.png.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c3upx1jyltix0" +path.s3tc="res://.godot/imported/mann_0.png-0eec56285f3b8080c8323d0defc178e9.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} +generator_parameters={ +"md5": "8af17aa62c9ee95592da5f3e6e42fcf1" +} + +[deps] + +source_file="res://assets/mann_0.png" +dest_files=["res://.godot/imported/mann_0.png-0eec56285f3b8080c8323d0defc178e9.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +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=0 diff --git a/godot/assets/mann_1.png b/godot/assets/mann_1.png new file mode 100644 index 0000000..f911734 Binary files /dev/null and b/godot/assets/mann_1.png differ diff --git a/godot/assets/mann_1.png.import b/godot/assets/mann_1.png.import new file mode 100644 index 0000000..9d16ce2 --- /dev/null +++ b/godot/assets/mann_1.png.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b14nebibcf2g6" +path.s3tc="res://.godot/imported/mann_1.png-93592f6ed267c9cdc1a955fda87a4e91.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} +generator_parameters={ +"md5": "018299a57123ad32d2eaf390dda03ca7" +} + +[deps] + +source_file="res://assets/mann_1.png" +dest_files=["res://.godot/imported/mann_1.png-93592f6ed267c9cdc1a955fda87a4e91.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=1 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=1 +roughness/src_normal="res://assets/mann_1.png" +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=0 diff --git a/godot/scenes/enemy.tscn b/godot/scenes/enemy.tscn new file mode 100644 index 0000000..dd3a8a8 --- /dev/null +++ b/godot/scenes/enemy.tscn @@ -0,0 +1,85 @@ +[gd_scene load_steps=10 format=3 uid="uid://c0cf6jkxasplk"] + +[ext_resource type="PackedScene" uid="uid://cyrwuqefk3efr" path="res://assets/mann.glb" id="2_4ra3w"] +[ext_resource type="Script" uid="uid://d4anpy5efe22d" path="res://src/health.gd" id="2_md0e3"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_md0e3"] + +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_md0e3"] +animation = &"HeadHit" + +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_4ra3w"] +animation = &"DyingBackwards" + +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_5uy6h"] +animation = &"T-Pose (No Animation)" + +[sub_resource type="AnimationNodeOneShot" id="AnimationNodeOneShot_c4w8v"] + +[sub_resource type="AnimationNodeTransition" id="AnimationNodeTransition_xwavj"] +input_0/name = "idle" +input_0/auto_advance = false +input_0/break_loop_at_end = false +input_0/reset = true +input_1/name = "dead" +input_1/auto_advance = false +input_1/break_loop_at_end = false +input_1/reset = true + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_nenq2"] +graph_offset = Vector2(-685.25, 50.2044) +nodes/Animation/node = SubResource("AnimationNodeAnimation_5uy6h") +nodes/Animation/position = Vector2(-220, 120) +"nodes/Animation 2/node" = SubResource("AnimationNodeAnimation_md0e3") +"nodes/Animation 2/position" = Vector2(-140, 320) +"nodes/Animation 3/node" = SubResource("AnimationNodeAnimation_4ra3w") +"nodes/Animation 3/position" = Vector2(60, 420) +nodes/Hit/node = SubResource("AnimationNodeOneShot_c4w8v") +nodes/Hit/position = Vector2(60, 120) +nodes/Transition/node = SubResource("AnimationNodeTransition_xwavj") +nodes/Transition/position = Vector2(260, 120) +nodes/output/position = Vector2(440, 120) +node_connections = [&"Hit", 0, &"Animation", &"Hit", 1, &"Animation 2", &"Transition", 0, &"Hit", &"Transition", 1, &"Animation 3", &"output", 0, &"Transition"] + +[node name="Enemy" type="CharacterBody3D"] +transform = Transform3D(1.75, 0, 0, 0, 1.75, 0, 0, 0, 1.75, 0, 0, 0) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +shape = SubResource("CapsuleShape3D_md0e3") + +[node name="Health" type="Node" parent="."] +script = ExtResource("2_md0e3") +max_health = 5 +metadata/_custom_type_script = "uid://d4anpy5efe22d" + +[node name="AnimationTree" type="AnimationTree" parent="."] +root_node = NodePath("../AuxScene") +tree_root = SubResource("AnimationNodeBlendTree_nenq2") +anim_player = NodePath("../AuxScene/AnimationPlayer") +parameters/Hit/active = false +parameters/Hit/internal_active = false +parameters/Hit/request = 0 +parameters/Transition/current_state = "idle" +parameters/Transition/transition_request = "" +parameters/Transition/current_index = 0 + +[node name="AuxScene" parent="." instance=ExtResource("2_4ra3w")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.708055, 0) + +[node name="Skeleton3D" parent="AuxScene/Node" index="0"] +bones/14/rotation = Quaternion(-0.049153, -0.00278018, 0.056403, 0.997194) +bones/30/rotation = Quaternion(-0.00214526, -6.9426e-18, 1.71983e-18, 0.999998) +bones/33/rotation = Quaternion(0.0397321, 6.93342e-18, -2.75697e-19, 0.99921) +bones/39/rotation = Quaternion(0.0548733, -0.109425, -0.0279963, 0.992084) +bones/45/rotation = Quaternion(0.0420007, -5.82877e-19, -1.38655e-17, 0.999118) +bones/46/rotation = Quaternion(-0.00160061, -2.22129e-20, 1.38778e-17, 0.999999) +bones/50/rotation = Quaternion(-0.027251, 0, 0, 0.999629) +bones/52/rotation = Quaternion(0.0706924, 0.00317808, 0.0447985, 0.996487) +bones/56/rotation = Quaternion(0.0852392, 0.00496034, 0.0578828, 0.994665) +bones/61/rotation = Quaternion(-0.00265969, 1.04465e-05, -0.0039276, 0.999989) + +[connection signal="damaged" from="Health" to="AnimationTree" method="set" binds= ["parameters/Hit/request", 1]] +[connection signal="died" from="Health" to="AnimationTree" method="set" binds= ["parameters/Transition/transition_request", "dead"]] + +[editable path="AuxScene"] diff --git a/godot/scenes/level.tscn b/godot/scenes/level.tscn index c39117b..29e96fc 100644 --- a/godot/scenes/level.tscn +++ b/godot/scenes/level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=15 format=3 uid="uid://dttyp3682enn7"] +[gd_scene load_steps=16 format=3 uid="uid://dttyp3682enn7"] [ext_resource type="PackedScene" uid="uid://crbrniwi6kd3p" path="res://scenes/player.tscn" id="1_2q6dc"] [ext_resource type="Script" uid="uid://csjccrhj5wnx7" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd" id="2_klq6b"] @@ -9,6 +9,7 @@ [ext_resource type="Script" uid="uid://cdvtrsqkkxtdd" path="res://src/persistence.gd" id="4_mx8sn"] [ext_resource type="Resource" uid="uid://cqfnwpmo4fyv4" path="res://resources/items/key.tres" id="7_8c41q"] [ext_resource type="PackedScene" uid="uid://cn7tgd4y67wnd" path="res://scenes/inventory.tscn" id="8_b121j"] +[ext_resource type="PackedScene" uid="uid://c0cf6jkxasplk" path="res://scenes/enemy.tscn" id="10_olg7q"] [sub_resource type="PlaneMesh" id="PlaneMesh_rd3vj"] @@ -113,6 +114,10 @@ text = "Load [node name="Inventory" parent="CanvasLayer" instance=ExtResource("8_b121j")] visible = false +[node name="Enemy" parent="." instance=ExtResource("10_olg7q")] +transform = Transform3D(1.75, 0, 0, 0, 1.75, 0, 0, 0, 1.75, 6, 0, 0) + +[connection signal="opened" from="Door" to="Door" method="hide"] [connection signal="interacted" from="Door/Interactable" to="Door" method="_on_interact"] [connection signal="pressed" from="CanvasLayer/Control/HBoxContainer/SaveButton" to="CanvasLayer/Control/Persistence" method="save" binds= ["save1.sav"]] [connection signal="pressed" from="CanvasLayer/Control/HBoxContainer/LoadButton" to="CanvasLayer/Control/Persistence" method="load" binds= ["save1.sav"]] diff --git a/godot/scenes/pistol.tscn b/godot/scenes/pistol.tscn new file mode 100644 index 0000000..10527a0 --- /dev/null +++ b/godot/scenes/pistol.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=4 format=3 uid="uid://isqkayrtr7t8"] + +[ext_resource type="Script" uid="uid://bn7dr8s404qnf" path="res://src/usable.gd" id="1_1e3ry"] +[ext_resource type="PackedScene" uid="uid://5a023hrws3gx" path="res://assets/Glock 17 Gen 4/Main.glb" id="1_igbvm"] +[ext_resource type="Script" uid="uid://ddqif2gmm1ec2" path="res://src/damage_source.gd" id="2_buff3"] + +[node name="Pistol" type="Node3D"] +script = ExtResource("1_1e3ry") + +[node name="Main" parent="." instance=ExtResource("1_igbvm")] + +[node name="RayCast3D" type="RayCast3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, -1.25) +target_position = Vector3(0, 0, -100) +debug_shape_custom_color = Color(1, 0, 0, 1) +debug_shape_thickness = 1 + +[node name="DamageSource" type="Node" parent="." node_paths=PackedStringArray("raycast")] +script = ExtResource("2_buff3") +damage = 1 +raycast = NodePath("../RayCast3D") +metadata/_custom_type_script = "uid://ddqif2gmm1ec2" + +[node name="Timer" type="Timer" parent="."] +wait_time = 0.5 +one_shot = true + +[connection signal="used" from="." to="." method="disable"] +[connection signal="used" from="." to="DamageSource" method="try_damage"] +[connection signal="used" from="." to="Timer" method="start"] +[connection signal="timeout" from="Timer" to="." method="enable"] diff --git a/godot/scenes/player.tscn b/godot/scenes/player.tscn index c97ccb2..a090346 100644 --- a/godot/scenes/player.tscn +++ b/godot/scenes/player.tscn @@ -1,11 +1,13 @@ -[gd_scene load_steps=44 format=3 uid="uid://crbrniwi6kd3p"] +[gd_scene load_steps=49 format=3 uid="uid://crbrniwi6kd3p"] [ext_resource type="PackedScene" uid="uid://dpmbimh6m4ari" path="res://scenes/player_mesh.tscn" id="1_3vyb7"] -[ext_resource type="Script" uid="uid://d1drx1b1s1evy" path="res://src/player.gd" id="1_qhqgy"] +[ext_resource type="Script" uid="uid://50vv0ta67tgl" path="res://src/player.gd" id="1_qhqgy"] [ext_resource type="Script" uid="uid://877g2wvcupw6" path="res://src/player_input.gd" id="3_dqkch"] [ext_resource type="Script" uid="uid://c3wlcxy4vnm2i" path="res://src/interactor.gd" id="4_dqkch"] [ext_resource type="Script" uid="uid://csjl56hf0fnoy" path="res://src/item_container.gd" id="5_qlg0r"] [ext_resource type="Resource" uid="uid://bllq6ri54q3ne" path="res://resources/player_inventory.tres" id="6_tuyoq"] +[ext_resource type="PackedScene" uid="uid://isqkayrtr7t8" path="res://scenes/pistol.tscn" id="7_fjrip"] +[ext_resource type="Script" uid="uid://c710qg683rqbc" path="res://src/equipment.gd" id="7_ur7pv"] [sub_resource type="SphereShape3D" id="SphereShape3D_qhqgy"] @@ -14,6 +16,27 @@ [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_smehm"] animation = &"Aim" +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_jej6c"] +animation = &"Fire" + +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_f1ej7"] +animation = &"Aim" + +[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_oprun"] +filter_enabled = true +filters = ["AuxScene/Scene/Armature/Skeleton3D:mixamorigHead", "AuxScene/Scene/Armature/Skeleton3D:mixamorigHead001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigHips", "AuxScene/Scene/Armature/Skeleton3D:mixamorigHips001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftArm", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftFoot", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftForeArm", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftLeg", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftLeg001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftShoulder", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftShoulder001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftToeBase", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftUpLeg", "AuxScene/Scene/Armature/Skeleton3D:mixamorigLeftUpLeg001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigNeck", "AuxScene/Scene/Armature/Skeleton3D:mixamorigNeck001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightArm", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightFoot", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightForeArm", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightLeg", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightLeg001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightShoulder", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightShoulder001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightToeBase", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightUpLeg", "AuxScene/Scene/Armature/Skeleton3D:mixamorigRightUpLeg001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigSpine", "AuxScene/Scene/Armature/Skeleton3D:mixamorigSpine001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigSpine1", "AuxScene/Scene/Armature/Skeleton3D:mixamorigSpine1001", "AuxScene/Scene/Armature/Skeleton3D:mixamorigSpine2", "AuxScene/Scene/Armature/Skeleton3D:mixamorigSpine2001"] + +[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_a8ls1"] +graph_offset = Vector2(-454.653, 56.3387) +nodes/Animation/node = SubResource("AnimationNodeAnimation_f1ej7") +nodes/Animation/position = Vector2(200, 340) +"nodes/Animation 2/node" = SubResource("AnimationNodeAnimation_jej6c") +"nodes/Animation 2/position" = Vector2(200, 160) +nodes/Blend2/node = SubResource("AnimationNodeBlend2_oprun") +nodes/Blend2/position = Vector2(380, 160) +nodes/output/position = Vector2(560, 160) +node_connections = [&"Blend2", 0, &"Animation 2", &"Blend2", 1, &"Animation", &"output", 0, &"Blend2"] + [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_d2wvv"] animation = &"Run" @@ -35,9 +58,6 @@ nodes/Blend2/position = Vector2(60, 140) nodes/output/position = Vector2(300, 140) node_connections = [&"Blend2", 0, &"Animation", &"Blend2", 1, &"Animation 2", &"output", 0, &"Blend2"] -[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_ur7pv"] -animation = &"Fire" - [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_y4r1p"] animation = &"PistolIdle" @@ -76,14 +96,6 @@ advance_expression = "not velocity" advance_mode = 2 advance_expression = "is_weapon_ready" -[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_a8ls1"] -advance_mode = 2 -advance_expression = "is_firing" - -[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_qfm1y"] -switch_mode = 2 -advance_mode = 2 - [sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_fulsm"] advance_mode = 2 advance_expression = "not is_weapon_ready" @@ -96,19 +108,30 @@ advance_expression = "is_running" advance_mode = 2 advance_expression = "not is_running" +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_a8ls1"] +advance_mode = 2 +advance_expression = "is_firing +" + +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_qfm1y"] +break_loop_at_end = true +switch_mode = 2 +advance_mode = 2 + [sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_d2wvv"] states/Aim/node = SubResource("AnimationNodeAnimation_smehm") states/Aim/position = Vector2(327, 194) states/BlendTree/node = SubResource("AnimationNodeBlendTree_f1ej7") states/BlendTree/position = Vector2(653, 100) +"states/BlendTree 2/node" = SubResource("AnimationNodeBlendTree_a8ls1") +"states/BlendTree 2/position" = Vector2(488, 194) states/End/position = Vector2(890, 40) -states/Fire/node = SubResource("AnimationNodeAnimation_ur7pv") -states/Fire/position = Vector2(462, 194) states/PistolIdle/node = SubResource("AnimationNodeAnimation_y4r1p") states/PistolIdle/position = Vector2(327, 100) states/Walk/node = SubResource("AnimationNodeBlendTree_ur7pv") states/Walk/position = Vector2(488, 100) -transitions = ["Start", "PistolIdle", SubResource("AnimationNodeStateMachineTransition_ur7pv"), "PistolIdle", "Walk", SubResource("AnimationNodeStateMachineTransition_y4r1p"), "Walk", "PistolIdle", SubResource("AnimationNodeStateMachineTransition_d2wvv"), "PistolIdle", "Aim", SubResource("AnimationNodeStateMachineTransition_oprun"), "Aim", "Fire", SubResource("AnimationNodeStateMachineTransition_a8ls1"), "Fire", "Aim", SubResource("AnimationNodeStateMachineTransition_qfm1y"), "Aim", "PistolIdle", SubResource("AnimationNodeStateMachineTransition_fulsm"), "Walk", "BlendTree", SubResource("AnimationNodeStateMachineTransition_4r5pv"), "BlendTree", "Walk", SubResource("AnimationNodeStateMachineTransition_60mlk")] +transitions = ["Start", "PistolIdle", SubResource("AnimationNodeStateMachineTransition_ur7pv"), "PistolIdle", "Walk", SubResource("AnimationNodeStateMachineTransition_y4r1p"), "Walk", "PistolIdle", SubResource("AnimationNodeStateMachineTransition_d2wvv"), "PistolIdle", "Aim", SubResource("AnimationNodeStateMachineTransition_oprun"), "Aim", "PistolIdle", SubResource("AnimationNodeStateMachineTransition_fulsm"), "Walk", "BlendTree", SubResource("AnimationNodeStateMachineTransition_4r5pv"), "BlendTree", "Walk", SubResource("AnimationNodeStateMachineTransition_60mlk"), "Aim", "BlendTree 2", SubResource("AnimationNodeStateMachineTransition_a8ls1"), "BlendTree 2", "Aim", SubResource("AnimationNodeStateMachineTransition_qfm1y")] +graph_offset = Vector2(-75, 65) [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_qhqgy"] animation = &"Idle" @@ -170,12 +193,31 @@ states/Unarmed/node = SubResource("AnimationNodeStateMachine_ur7pv") states/Unarmed/position = Vector2(356, 50) transitions = ["Start", "Unarmed", SubResource("AnimationNodeStateMachineTransition_qlg0r"), "Start", "Handgun", SubResource("AnimationNodeStateMachineTransition_3v2ag"), "Handgun", "Unarmed", SubResource("AnimationNodeStateMachineTransition_jej6c"), "Unarmed", "Handgun", SubResource("AnimationNodeStateMachineTransition_f1ej7")] -[node name="Player" type="CharacterBody3D" groups=["persist"]] +[node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("input", "interactor", "equipment") groups=["persist"]] script = ExtResource("1_qhqgy") +input = NodePath("Input") +interactor = NodePath("Interactor") +equipment = NodePath("Weapon") [node name="Input" type="Node" parent="."] script = ExtResource("3_dqkch") +[node name="Inventory" type="Node" parent="."] +script = ExtResource("5_qlg0r") +inventory = ExtResource("6_tuyoq") + +[node name="Weapon" type="BoneAttachment3D" parent="." node_paths=PackedStringArray("initial_equipment")] +transform = Transform3D(0.00876506, 0.0120414, 0.00178277, 0.00882159, -0.00779736, 0.00929411, 0.00838767, -0.00438243, -0.0116379, -0.128585, 1.36723, 0.500051) +bone_name = "mixamorigRightHandMiddle1" +bone_idx = 24 +use_external_skeleton = true +external_skeleton = NodePath("../Mesh/AuxScene/Scene/Armature/Skeleton3D") +script = ExtResource("7_ur7pv") +initial_equipment = NodePath("Pistol") + +[node name="Pistol" parent="Weapon" instance=ExtResource("7_fjrip")] +transform = Transform3D(-0.948472, 9.81445, -1.66645, -5.38265, -1.91379, -8.20758, -8.37422, 0.118531, 5.46429, 3.16379, 3.48907, 1.04434) + [node name="Interactor" type="Node3D" parent="." node_paths=PackedStringArray("area", "root_node")] script = ExtResource("4_dqkch") area = NodePath("Area3D") @@ -200,10 +242,7 @@ tree_root = SubResource("AnimationNodeStateMachine_y4r1p") advance_expression_base_node = NodePath("..") anim_player = NodePath("../Mesh/AnimationPlayer") parameters/Handgun/BlendTree/Blend2/blend_amount = 1.0 +"parameters/Handgun/BlendTree 2/Blend2/blend_amount" = 1.0 parameters/Handgun/Walk/Blend2/blend_amount = 1.0 -[node name="ItemContainer" type="Node" parent="."] -script = ExtResource("5_qlg0r") -inventory = ExtResource("6_tuyoq") - [editable path="Mesh"] diff --git a/godot/src/damage_source.gd b/godot/src/damage_source.gd new file mode 100644 index 0000000..c42bc57 --- /dev/null +++ b/godot/src/damage_source.gd @@ -0,0 +1,25 @@ +class_name DamageSource extends Node + +@export var damage: int +@export var raycast: RayCast3D + +func _ready() -> void: + raycast.enabled = false + +func raycast_to_target() -> Option: + raycast.force_raycast_update() + if raycast.is_colliding(): + return Option.some(raycast.get_collider()) + else: + return Option.none + +func try_damage() -> void: + match raycast_to_target(): + var option when option.is_some(): + var value = option.unwrap() + match NodeExt.find_child_variant(value, Health): + var node when node.is_some(): + var health: Health = node.unwrap() + health.apply_damage(damage) + _: pass + _: pass diff --git a/godot/src/damage_source.gd.uid b/godot/src/damage_source.gd.uid new file mode 100644 index 0000000..3cb6a5e --- /dev/null +++ b/godot/src/damage_source.gd.uid @@ -0,0 +1 @@ +uid://ddqif2gmm1ec2 diff --git a/godot/src/equipment.gd b/godot/src/equipment.gd new file mode 100644 index 0000000..a05c95b --- /dev/null +++ b/godot/src/equipment.gd @@ -0,0 +1,25 @@ +class_name Equipment extends Node + +signal unequipped(item: Item) +signal equipped(item: Item) + +@export var initial_equipment: Usable +var current_item: Option = Option.none + +func _ready() -> void: + current_item = Option.from(initial_equipment) + +func unequip(): + if current_item.is_some(): + var item = current_item.unwrap() + current_item = Option.none + unequipped.emit(item) + +func equip(item: Item): + unequip() + if item != null: + current_item = Option.some(item) + equipped.emit(item) + +func use(): + current_item.inspect(func(x: Variant) -> void: x.use()) diff --git a/godot/src/equipment.gd.uid b/godot/src/equipment.gd.uid new file mode 100644 index 0000000..9366656 --- /dev/null +++ b/godot/src/equipment.gd.uid @@ -0,0 +1 @@ +uid://c710qg683rqbc diff --git a/godot/src/health.gd b/godot/src/health.gd new file mode 100644 index 0000000..1ca3e40 --- /dev/null +++ b/godot/src/health.gd @@ -0,0 +1,26 @@ +class_name Health extends Node + +signal died +signal damaged + +@export var max_health: int +@export var current_value: int +@export var heal_on_spawn: bool = true + +var dead: bool = false + +func _ready() -> void: + if heal_on_spawn: + current_value = max_health + +func apply_damage(value: int): + if dead: return + current_value = max(current_value - value, 0) + if current_value <= 0: + dead = true + died.emit() + else: + damaged.emit() + +func apply_healing(value: int): + current_value = min(current_value + value, max_health) diff --git a/godot/src/health.gd.uid b/godot/src/health.gd.uid new file mode 100644 index 0000000..e1d3cc1 --- /dev/null +++ b/godot/src/health.gd.uid @@ -0,0 +1 @@ +uid://d4anpy5efe22d diff --git a/godot/src/node_ext.gd b/godot/src/node_ext.gd index b5c17c7..39f3b18 100644 --- a/godot/src/node_ext.gd +++ b/godot/src/node_ext.gd @@ -1,38 +1,38 @@ class_name NodeExt class NodeChildIterator: - var _node: Node - var _include_internal: bool - var _index: int - var _length: int + var _node: Node + var _include_internal: bool + var _index: int + var _length: int - func _init(node: Node, include_internal: bool) -> void: - _node = node - _include_internal = include_internal - _index = 0 - _length = node.get_child_count(include_internal) + func _init(node: Node, include_internal: bool) -> void: + _node = node + _include_internal = include_internal + _index = 0 + _length = node.get_child_count(include_internal) - func _continue() -> bool: - return (_index < _length) + func _continue() -> bool: + return (_index < _length) - func _iter_init(_iter: Array) -> bool: - _index = 0 - return _continue() + func _iter_init(_iter: Array) -> bool: + _index = 0 + return _continue() - func _iter_next(_iter: Array) -> bool: - _index += 1 - return _continue() + func _iter_next(_iter: Array) -> bool: + _index += 1 + return _continue() - func _iter_get(_iter: Variant) -> Variant: - return _node.get_child(_index, _include_internal) + func _iter_get(_iter: Variant) -> Variant: + return _node.get_child(_index, _include_internal) static func find_child(node: Node, predicate: Callable, include_internal: bool = false) -> Option: - for child in NodeChildIterator.new(node, include_internal): - if predicate.call(child): - return Option.some(child) - return Option.none + for child in NodeChildIterator.new(node, include_internal): + if predicate.call(child): + return Option.some(child) + return Option.none static func find_child_variant(node: Node, variant: Variant, include_internal: bool = false) -> Option: - for child in NodeChildIterator.new(node, include_internal): - if is_instance_of(child, variant): - return Option.some(child) - return Option.none + for child in NodeChildIterator.new(node, include_internal): + if is_instance_of(child, variant): + return Option.some(child) + return Option.none diff --git a/godot/src/player.gd b/godot/src/player.gd index 26bbca9..b5f5002 100644 --- a/godot/src/player.gd +++ b/godot/src/player.gd @@ -3,55 +3,56 @@ class_name Player extends CharacterBody3D @export var walk_speed: float = 4.0 @export var run_speed: float = 6.0 -@onready var input = $Input -@onready var interactor = $Interactor +@export var input: PlayerInput +@export var interactor: Interactor +@export var equipment: Equipment var is_running: bool: - get: return velocity and input.is_running + get: return velocity and input.is_running var is_carrying_item = true var is_weapon_ready: bool: - get: return is_carrying_item and input.is_weapon_ready + get: return is_carrying_item and input.is_weapon_ready func _ready() -> void: - input.connect('interact', _on_interact) - input.connect('fire', _on_fire) + input.connect('interact', _on_interact) + input.connect('fire', _on_fire) func _physics_process(delta: float) -> void: - if is_weapon_ready: - rotate_toward_look(delta) - else: - move_and_rotate(delta) + if is_weapon_ready: + rotate_toward_look(delta) + else: + move_and_rotate(delta) func move_and_rotate(_delta: float): - var speed = run_speed if input.is_running else walk_speed - velocity = input.next_velocity(speed) - if !velocity.is_zero_approx(): - look_at(global_position + velocity, Vector3.UP, true) - move_and_slide() + var speed = run_speed if input.is_running else walk_speed + velocity = input.next_velocity(speed) + if !velocity.is_zero_approx(): + look_at(global_position + velocity, Vector3.UP, true) + move_and_slide() func rotate_toward_look(_delta: float): - velocity = Vector3.ZERO - var target = input.get_look_target(global_position) - if target.is_some(): - look_at(target.unwrap(), Vector3.UP, true) + velocity = Vector3.ZERO + var target = input.get_look_target(global_position) + if target.is_some(): + look_at(target.unwrap(), Vector3.UP, true) func _on_interact(): - interactor.interact_nearest() + interactor.interact_nearest() func _on_fire(): - if is_weapon_ready: - print('firing weapon') + if is_weapon_ready: + equipment.use() func on_save(): - return { - position = position, - rotation = rotation - } + return { + position = position, + rotation = rotation + } func on_before_load(): - pass + pass func on_load(data: Dictionary): - position = data.position - rotation = data.rotation + position = data.position + rotation = data.rotation diff --git a/godot/src/raycast.gd.uid b/godot/src/raycast.gd.uid new file mode 100644 index 0000000..73aefc6 --- /dev/null +++ b/godot/src/raycast.gd.uid @@ -0,0 +1 @@ +uid://bgun1u5812uil diff --git a/godot/src/usable.gd b/godot/src/usable.gd new file mode 100644 index 0000000..a2049dc --- /dev/null +++ b/godot/src/usable.gd @@ -0,0 +1,18 @@ +class_name Usable extends Node + +signal used + +@export var enabled: bool = true + +func enable() -> void: + enabled = true + +func disable() -> void: + enabled = false + +func set_enabled(is_enabled: bool) -> void: + self.enabled = is_enabled + +func use(): + if enabled: + used.emit() diff --git a/godot/src/usable.gd.uid b/godot/src/usable.gd.uid new file mode 100644 index 0000000..4eb3045 --- /dev/null +++ b/godot/src/usable.gd.uid @@ -0,0 +1 @@ +uid://bn7dr8s404qnf