Compare commits

...

5 commits

20 changed files with 6447 additions and 49 deletions

View file

@ -0,0 +1,47 @@
#include "damage_box.h"
#include "health_status.h"
#include "hitbox.h"
void DamageBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("attack_motion_begin"), &self_type::attack_motion_begin);
ClassDB::bind_method(D_METHOD("attack_motion_end"), &self_type::attack_motion_end);
}
void DamageBox::attack_motion_begin() {
set_monitoring(true);
}
void DamageBox::attack_motion_end() {
this->already_hit.clear();
set_monitoring(true);
}
void DamageBox::on_body_entered(Node3D *node) {
if (Hitbox * box{ cast_to<Hitbox>(node) }) {
if (!this->already_hit.has(box->get_health())) {
box->get_health()->damage(this->damage * box->get_damage_modifier());
this->already_hit.insert(box->get_health());
}
}
}
void DamageBox::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
switch (what) {
default:
return;
case NOTIFICATION_ENTER_TREE:
connect("body_entered", callable_mp(this, &self_type::on_body_entered));
return;
}
}
void DamageBox::set_damage(int amount) {
this->damage = amount;
}
int DamageBox::get_damage() const {
return this->damage;
}

View file

@ -0,0 +1,27 @@
#ifndef DAMAGE_BOX_H
#define DAMAGE_BOX_H
#include "core/templates/hash_set.h"
#include "scene/3d/physics/area_3d.h"
class HealthStatus;
class DamageBox : public Area3D {
GDCLASS(DamageBox, Area3D);
static void _bind_methods();
void attack_motion_begin();
void attack_motion_end();
void on_body_entered(Node3D *body);
protected:
void _notification(int what);
public:
void set_damage(int amount);
int get_damage() const;
private:
int damage{ 1 };
HashSet<HealthStatus *> already_hit{};
};
#endif // !DAMAGE_BOX_H

View file

@ -1,4 +1,5 @@
#include "enemy_wretched.h"
#include "scene/animation/animation_player.h"
#include "wave_survival/npc_unit.h"
#include "wave_survival/patrol_path.h"
#include "wave_survival/player_body.h"
@ -7,13 +8,20 @@
void EnemyWretched::_bind_methods() {
}
void EnemyWretched::on_child_entered(Node *node) {
if (StateMachine * fsm{ cast_to<StateMachine>(node) }) {
this->fsm = fsm;
}
if (node->has_node(NodePath("AnimationPlayer"))) {
this->anim = cast_to<AnimationPlayer>(node->get_node(NodePath("AnimationPlayer")));
}
}
void EnemyWretched::ready() {
if (StateMachine * fsm{ cast_to<StateMachine>(get_node(NodePath("%StateMachine"))) }) {
print_line("ready");
fsm->add_state(memnew(WretchedPatrolState));
fsm->add_state(memnew(WretchedChaseState));
fsm->add_state(memnew(WretchedAttackState));
}
}
void EnemyWretched::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) {
@ -22,16 +30,21 @@ void EnemyWretched::_notification(int what) {
switch (what) {
default:
return;
case NOTIFICATION_ENTER_TREE:
connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered));
return;
case NOTIFICATION_READY:
ready();
return;
}
}
AnimationPlayer *EnemyWretched::get_anim() const {
return this->anim;
}
void WretchedState::set_target(Node *node) {
this->target = cast_to<EnemyWretched>(node);
this->nav = this->target->get_nav();
this->unit = this->target->get_unit();
}
EnemyWretched *WretchedState::get_target() const {
@ -39,11 +52,15 @@ EnemyWretched *WretchedState::get_target() const {
}
NpcUnit *WretchedState::get_unit() const {
return this->unit;
return this->target->get_unit();
}
NavigationAgent3D *WretchedState::get_nav() const {
return this->nav;
return this->target->get_nav();
}
AnimationPlayer *WretchedState::get_anim() const {
return this->target->get_anim();
}
void WretchedPatrolState::set_patrol_target(Vector3 path_point) {
@ -85,6 +102,7 @@ void WretchedChaseState::enter_state() {
get_target()->set_movement_speed(get_unit()->get_patrol_speed() * 2.f);
get_nav()->set_max_speed(get_unit()->get_patrol_speed() * 2.f);
get_nav()->set_target_position(PlayerBody::get_singleton()->get_global_position());
get_anim()->play("ready"); // TODO: replace this with "run"
}
void WretchedChaseState::process(double delta) {
@ -101,6 +119,13 @@ String WretchedChaseState::get_next_state() const {
return get_class();
}
void WretchedAttackState::enter_state() {
get_anim()->play("attack");
}
String WretchedAttackState::get_next_state() const {
if (get_anim()->get_current_animation().is_empty()) {
return WretchedChaseState::get_class_static();
}
return get_class();
}

View file

@ -5,14 +5,24 @@
#include "wave_survival/state.h"
class PatrolPath;
class NpcUnit;
class AnimationPlayer;
class StateMachine;
class EnemyWretched : public EnemyBody {
GDCLASS(EnemyWretched, EnemyBody);
static void _bind_methods();
void on_child_entered(Node *node);
void ready();
protected:
void _notification(int what);
public:
AnimationPlayer *get_anim() const;
private:
StateMachine *fsm{ nullptr };
AnimationPlayer *anim{ nullptr };
};
/* ============================== STATES ============================== */
@ -26,11 +36,10 @@ public:
EnemyWretched *get_target() const;
NpcUnit *get_unit() const;
NavigationAgent3D *get_nav() const;
AnimationPlayer *get_anim() const;
private:
NpcUnit *unit{ nullptr };
EnemyWretched *target{ nullptr };
NavigationAgent3D *nav{ nullptr };
};
class WretchedPatrolState : public WretchedState {
@ -63,6 +72,7 @@ class WretchedAttackState : public WretchedState {
static void _bind_methods() {}
public:
virtual void enter_state() override;
virtual String get_next_state() const override;
};

View file

@ -1,4 +1,5 @@
#include "player_body.h"
#include "health_status.h"
#include "macros.h"
#include "player_input.h"
#include "weapon_base.h"
@ -9,16 +10,19 @@ PlayerBody *PlayerBody::singleton_instance{ nullptr };
void PlayerBody::_bind_methods() {
}
void PlayerBody::ready() {
PlayerInput *input{ cast_to<PlayerInput>(get_node(NodePath("%PlayerInput"))) };
void PlayerBody::on_child_entered(Node *node) {
if (PlayerInput * input{ cast_to<PlayerInput>(node) }) {
input->connect(PlayerInput::sig_movement_input, callable_mp(this, &self_type::set_movement_input));
input->connect(PlayerInput::sig_look_input, callable_mp(this, &self_type::on_look_input));
input->connect(PlayerInput::sig_jump, callable_mp(this, &self_type::on_jump_input));
input->connect(PlayerInput::sig_run, callable_mp(this, &self_type::on_run_input));
this->weapons = cast_to<WeaponInventory>(get_node(NodePath("%WeaponInventory")));
}
void PlayerBody::process(double delta) {
if (WeaponInventory * inventory{ cast_to<WeaponInventory>(node) }) {
this->weapons = inventory;
}
if (HealthStatus * health{ cast_to<HealthStatus>(node) }) {
this->health = health;
}
}
void PlayerBody::physics_process(double delta) {
@ -65,6 +69,7 @@ void PlayerBody::_notification(int what) {
return;
case NOTIFICATION_ENTER_TREE:
singleton_instance = this;
connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered));
return;
case NOTIFICATION_EXIT_TREE:
singleton_instance = nullptr;
@ -72,10 +77,6 @@ void PlayerBody::_notification(int what) {
case NOTIFICATION_READY:
set_process(true);
set_physics_process(true);
ready();
return;
case NOTIFICATION_PROCESS:
process(get_process_delta_time());
return;
case NOTIFICATION_PHYSICS_PROCESS:
physics_process(get_physics_process_delta_time());
@ -94,3 +95,7 @@ bool PlayerBody::get_is_running() const {
bool PlayerBody::get_wants_to_run() const {
return this->try_running && this->movement_input.y > 0.f && this->is_on_floor();
}
HealthStatus *PlayerBody::get_health() const {
return this->health;
}

View file

@ -3,15 +3,14 @@
#include "scene/3d/physics/character_body_3d.h"
class WeaponInventory;
class HealthStatus;
class PlayerBody : public CharacterBody3D {
GDCLASS(PlayerBody, CharacterBody3D);
static void _bind_methods();
static PlayerBody *singleton_instance;
private:
void ready();
void process(double delta);
void on_child_entered(Node *node);
void physics_process(double delta);
void set_movement_input(Vector2 state);
@ -26,6 +25,7 @@ public:
static PlayerBody *get_singleton();
bool get_is_running() const;
bool get_wants_to_run() const;
HealthStatus *get_health() const;
private:
bool try_running{ false };
@ -36,6 +36,7 @@ private:
Vector2 movement_input{};
WeaponInventory *weapons;
HealthStatus *health{ nullptr };
};
#endif // !PLAYER_BODY_H

View file

@ -1,6 +1,7 @@
#include "register_types.h"
#include "core/object/class_db.h"
#include "wave_survival/damage_box.h"
#include "wave_survival/enemies/enemy_wretched.h"
#include "wave_survival/enemy_body.h"
#include "wave_survival/health_status.h"
@ -41,6 +42,7 @@ void initialize_wave_survival_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(WretchedAttackState);
GDREGISTER_CLASS(PlayerDetector);
GDREGISTER_CLASS(Hitbox);
GDREGISTER_CLASS(DamageBox);
}
void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -15,9 +15,11 @@ dest_files=["res://.godot/imported/base_character.blend-06aab94c060939a7a7c95d6d
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
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
@ -32,6 +34,9 @@ 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
@ -51,3 +56,4 @@ blender/materials/export_materials=1
blender/animation/limit_playback=true
blender/animation/always_sample=true
blender/animation/group_tracks=true
gltf/naming_version=0

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -1,6 +1,6 @@
[gd_scene load_steps=7 format=3 uid="uid://dqlqgk1veyos8"]
[gd_scene load_steps=8 format=3 uid="uid://dqlqgk1veyos8"]
[ext_resource type="PackedScene" uid="uid://7esw31b76x4" path="res://assets/models/base_character.blend" id="1_qot2n"]
[ext_resource type="PackedScene" uid="uid://ho7wwawj6s65" path="res://assets/models/enemies/wretched.blend" id="2_ei3ai"]
[sub_resource type="GDScript" id="GDScript_qot2n"]
script/source = "extends EnemyWretched
@ -15,49 +15,67 @@ size = Vector3(0.37524414, 0.64245605, 0.38867188)
[sub_resource type="BoxShape3D" id="BoxShape3D_ei3ai"]
size = Vector3(0.24902344, 0.42858887, 0.2619629)
[sub_resource type="BoxShape3D" id="BoxShape3D_cpfor"]
size = Vector3(0.32873535, 0.107666016, 0.11816406)
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ng1ul"]
[sub_resource type="BoxShape3D" id="BoxShape3D_sivli"]
size = Vector3(0.2770996, 1.5324097, 0.29003906)
size = Vector3(0.35351563, 0.8721161, 0.29003906)
[node name="EnemyWretched" type="EnemyWretched"]
script = SubResource("GDScript_qot2n")
[node name="base_character" parent="." instance=ExtResource("1_qot2n")]
[node name="wretched" parent="." instance=ExtResource("2_ei3ai")]
[node name="SpineAttach" type="BoneAttachment3D" parent="base_character/Character/Skeleton3D" index="0"]
transform = Transform3D(1, -2.2202515e-16, 2.1910396e-16, 2.1910385e-16, 0.9999122, 0.0132439565, -2.2202522e-16, -0.013243958, 0.99991226, -4.125641e-17, 1.1546817, -0.002712473)
[node name="SpineAttach" type="BoneAttachment3D" parent="wretched/Character/Skeleton3D" index="2"]
transform = Transform3D(1, -2.2202518e-16, 2.1910394e-16, 2.1910388e-16, 0.9999122, 0.013243955, -2.220252e-16, -0.0132439565, 0.99991226, -4.1256417e-17, 1.1546816, -0.0027124744)
bone_name = "spine.002"
bone_idx = 3
[node name="Hitbox" type="Hitbox" parent="base_character/Character/Skeleton3D/SpineAttach" node_paths=PackedStringArray("health")]
[node name="Hitbox" type="Hitbox" parent="wretched/Character/Skeleton3D/SpineAttach" node_paths=PackedStringArray("health")]
transform = Transform3D(1, 2.1910389e-16, -2.2202524e-16, -2.2202514e-16, 0.9999124, -0.013243958, 2.1910394e-16, 0.01324396, 0.9999123, -2.1234107e-16, -1.1546164, -0.012580322)
collision_layer = 2
collision_layer = 6
collision_mask = 0
monitoring = false
health = NodePath("../../../../../HealthStatus")
[node name="CollisionShape3D" type="CollisionShape3D" parent="base_character/Character/Skeleton3D/SpineAttach/Hitbox"]
[node name="CollisionShape3D" type="CollisionShape3D" parent="wretched/Character/Skeleton3D/SpineAttach/Hitbox"]
transform = Transform3D(1, -2.2202515e-16, 2.1910396e-16, 2.1910385e-16, 0.9999122, 0.0132439565, -2.2202522e-16, -0.013243958, 0.99991226, 0.0023498535, 1.1967317, -0.0032694251)
shape = SubResource("BoxShape3D_qot2n")
[node name="HeadAttach" type="BoneAttachment3D" parent="base_character/Character/Skeleton3D" index="1"]
transform = Transform3D(1, -2.2186812e-16, 2.1319257e-16, 2.1319253e-16, 0.99920464, 0.039866537, -2.2186821e-16, -0.03986654, 0.99920475, -1.3069546e-16, 1.5574795, -0.00406873)
[node name="HeadAttach" type="BoneAttachment3D" parent="wretched/Character/Skeleton3D" index="3"]
transform = Transform3D(0.9999988, -0.0007823897, 0.0013344375, 0.0007271601, 0.99916273, 0.040897816, -0.0013653187, -0.040896796, 0.9991622, -1.3069549e-16, 1.5574794, -0.0040687313)
bone_name = "Neck"
bone_idx = 5
[node name="Hitbox" type="Hitbox" parent="base_character/Character/Skeleton3D/HeadAttach" node_paths=PackedStringArray("health")]
[node name="Hitbox" type="Hitbox" parent="wretched/Character/Skeleton3D/HeadAttach" node_paths=PackedStringArray("health")]
transform = Transform3D(1, 2.1910389e-16, -2.2202524e-16, -2.2202514e-16, 0.9999124, -0.013243958, 2.1910394e-16, 0.01324396, 0.9999123, -2.1234107e-16, -1.1546164, -0.012580322)
collision_layer = 2
collision_layer = 6
collision_mask = 0
monitoring = false
health = NodePath("../../../../../HealthStatus")
damage_modifier = 2.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="base_character/Character/Skeleton3D/HeadAttach/Hitbox"]
[node name="CollisionShape3D" type="CollisionShape3D" parent="wretched/Character/Skeleton3D/HeadAttach/Hitbox"]
transform = Transform3D(1, -2.2202515e-16, 2.1910393e-16, 2.1910723e-16, 0.9999161, 0.013243863, -2.2202641e-16, -0.013243858, 0.9999161, -7.644832e-17, 1.3035276, -0.01432839)
shape = SubResource("BoxShape3D_ei3ai")
[node name="BoneAttachment3D" type="BoneAttachment3D" parent="wretched/Character/Skeleton3D" index="4"]
transform = Transform3D(0.10797773, -0.79005337, 0.60345376, 0.4003794, 0.5901581, 0.70100564, -0.9099653, 0.16591746, 0.38004512, 0.28507733, 1.5014915, -0.21731633)
bone_name = "hand.R"
bone_idx = 9
[node name="Area3D" type="Area3D" parent="wretched/Character/Skeleton3D/BoneAttachment3D"]
transform = Transform3D(0.81687146, 0.42058486, 0.39475226, -0.47940338, 0.8755994, 0.059143633, -0.3207699, -0.2375583, 0.91688204, -0.0065350886, 0.06456717, -0.0151789)
collision_layer = 0
collision_mask = 10
monitorable = false
[node name="CollisionShape3D" type="CollisionShape3D" parent="wretched/Character/Skeleton3D/BoneAttachment3D/Area3D"]
transform = Transform3D(1, -5.722046e-06, -4.0163286e-08, 2.3841858e-07, 1.0000094, -2.0547304e-08, 2.438901e-07, 7.899827e-06, 1, -0.29290777, -2.9802322e-07, -2.3841858e-07)
shape = SubResource("BoxShape3D_cpfor")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("CapsuleShape3D_ng1ul")
@ -79,15 +97,15 @@ health = 2
[node name="Hitbox" type="Hitbox" parent="." node_paths=PackedStringArray("health")]
transform = Transform3D(1, 2.1910389e-16, -2.2202524e-16, -2.2202514e-16, 0.9999124, -0.013243958, 2.1910394e-16, 0.01324396, 0.9999123, 6.209e-17, 0.097789526, 0.004007945)
collision_layer = 2
collision_layer = 6
collision_mask = 0
monitoring = false
health = NodePath("../HealthStatus")
[node name="CollisionShape3D2" type="CollisionShape3D" parent="Hitbox"]
transform = Transform3D(1, 4.2740422e-21, 0, 6.2038546e-25, 1.0000037, 0, 0, 0, 1.0000037, -2.2362305e-16, 0.68298185, -0.013339132)
transform = Transform3D(1, 5.875464e-21, 0, 6.2038546e-25, 1.0000037, 0, 0, 0, 1.0000037, -2.2362435e-16, 0.35283393, -0.013339132)
shape = SubResource("BoxShape3D_sivli")
[connection signal="death" from="HealthStatus" to="." method="_on_health_status_death"]
[editable path="base_character"]
[editable path="wretched"]

View file

@ -1,9 +1,19 @@
[gd_scene load_steps=3 format=3 uid="uid://snjgu4yp5swd"]
[gd_scene load_steps=5 format=3 uid="uid://snjgu4yp5swd"]
[ext_resource type="PackedScene" uid="uid://ce40pq785yoyi" path="res://objects/weapons/rifle.tscn" id="1_eqqp1"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_bxedw"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_eqqp1"]
radius = 0.3173828
[sub_resource type="GDScript" id="GDScript_eqqp1"]
script/source = "extends HealthStatus
func _on_health_changed(remaining: int, delta: int) -> void:
print(\"Player Health Changed:\", delta, \"remaining:\", remaining)
"
[node name="PlayerBody" type="PlayerBody"]
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
@ -21,3 +31,18 @@ fov = 60.0
[node name="WeaponInventory" type="WeaponInventory" parent="."]
unique_name_in_owner = true
starting_weapon = ExtResource("1_eqqp1")
[node name="Hitbox" type="Hitbox" parent="." node_paths=PackedStringArray("health")]
collision_layer = 8
collision_mask = 0
monitoring = false
health = NodePath("../HealthStatus")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("CapsuleShape3D_eqqp1")
[node name="HealthStatus" type="HealthStatus" parent="."]
script = SubResource("GDScript_eqqp1")
[connection signal="health_changed" from="HealthStatus" to="HealthStatus" method="_on_health_changed"]

View file

@ -22,7 +22,7 @@ bone_idx = 39
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 0.99999833, -0.0017453281, 0, 0.0017453281, 0.99999833, 1.4540284e-26, 0, 0.027612558)
target_position = Vector3(0, 100, 0)
collision_mask = 2
collision_mask = 6
collide_with_areas = true
spread = 0.003

View file

@ -74,3 +74,5 @@ switch_weapon={
3d_render/layer_2="FirstPerson"
3d_physics/layer_1="General"
3d_physics/layer_2="Precision"
3d_physics/layer_3="Hitbox(Enemy)"
3d_physics/layer_4="Hitbox(Player)"