Compare commits

...

13 commits

21 changed files with 346 additions and 213 deletions

3
.gitignore vendored
View file

@ -22,3 +22,6 @@ build.zip
wave-survival-fps.kdev4 wave-survival-fps.kdev4
__pycache__ __pycache__
modules/wave_survival/__pycache__ modules/wave_survival/__pycache__
# blender auto backups
*.blend1

2
engine

@ -1 +1 @@
Subproject commit c6d130abd9188f313e6701d01a0ddd6ea32166a0 Subproject commit 084d5d407e62efcd5be9de44148c5dedce3b9386

View file

@ -4,7 +4,7 @@ BUILD_NAME := "wave_survival"
build: format build: format
# Compiling Editor # Compiling Editor
cd engine/ && scons target=editor symbols=yes optimization=debug dev_build=yes linker=mold use_llvm=yes custom_modules="../modules" cd engine/ && scons target=editor symbols=yes optimization=debug dev_build=yes linker=mold use_llvm=yes custom_modules="../modules" compiledb=yes
run: build run: build
# Running Editor # Running Editor

View file

@ -33,9 +33,11 @@ void DamageBox::_notification(int what) {
default: default:
return; return;
case NOTIFICATION_ENTER_TREE: case NOTIFICATION_ENTER_TREE:
if (!is_ready()) {
connect("body_entered", callable_mp(this, &self_type::on_body_entered)); connect("body_entered", callable_mp(this, &self_type::on_body_entered));
connect("area_entered", callable_mp(this, &self_type::on_body_entered)); connect("area_entered", callable_mp(this, &self_type::on_body_entered));
set_monitoring(false); set_monitoring(false);
}
return; return;
} }
} }

View file

@ -0,0 +1,26 @@
#include "enemy_rifleman.h"
void EnemyRifleman::_bind_methods() {
}
void EnemyRifleman::on_child_entered() {
}
void EnemyRifleman::ready() {
}
void EnemyRifleman::_notification(int what) {
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
switch (what) {
default:
return;
case NOTIFICATION_ENTER_TREE:
enter_tree();
return;
case NOTIFICATION_READY:
ready();
return;
}
}

View file

@ -2,10 +2,55 @@
#define ENEMY_RIFLEMAN_H #define ENEMY_RIFLEMAN_H
#include "wave_survival/enemy_body.h" #include "wave_survival/enemy_body.h"
#include "wave_survival/state.h"
class AnimationPlayer;
class NavigationAgent3D;
class EnemyRifleman : public EnemyBody { class EnemyRifleman : public EnemyBody {
GDCLASS(EnemyRifleman, EnemyBody); GDCLASS(EnemyRifleman, EnemyBody);
static void _bind_methods(); static void _bind_methods();
void on_child_entered();
void enter_tree();
void ready();
protected:
void _notification(int what);
};
/* ============================== STATES ============================== */
class RiflemanState : public State {
GDCLASS(RiflemanState, State);
static void _bind_methods();
public:
virtual void set_target(Node *target) override;
EnemyRifleman *get_target() const;
NpcUnit *get_unit() const;
NavigationAgent3D *get_agent() const;
AnimationPlayer *get_anim() const;
private:
EnemyRifleman *target{ nullptr };
};
class RiflemanPatrolState : public RiflemanState {
GDCLASS(RiflemanPatrolState, RiflemanState);
static void _bind_methods();
public:
virtual void enter_state() override;
virtual void process(double delta) override;
};
class RiflemanSeekState : public RiflemanState {
GDCLASS(RiflemanSeekState, RiflemanState);
static void _bind_methods();
};
class RiflemanFireState : public RiflemanState {
GDCLASS(RiflemanFireState, RiflemanState);
static void _bind_methods();
}; };
#endif // !ENEMY_RIFLEMAN_H #endif // !ENEMY_RIFLEMAN_H

View file

@ -33,7 +33,9 @@ void EnemyWretched::_notification(int what) {
default: default:
return; return;
case NOTIFICATION_ENTER_TREE: case NOTIFICATION_ENTER_TREE:
if (!is_ready()) {
connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered)); connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered));
}
return; return;
case NOTIFICATION_READY: case NOTIFICATION_READY:
ready(); ready();

View file

@ -19,11 +19,15 @@ void EnemyBody::on_child_added(Node *node) {
void EnemyBody::ready() { void EnemyBody::ready() {
this->fsm = cast_to<StateMachine>(get_node(NodePath("%StateMachine"))); this->fsm = cast_to<StateMachine>(get_node(NodePath("%StateMachine")));
this->nav = cast_to<NavigationAgent3D>(get_node(NodePath("%NavigationAgent3D"))); this->nav = cast_to<NavigationAgent3D>(get_node(NodePath("%NavigationAgent3D")));
this->health = cast_to<HealthStatus>(get_node(NodePath("%HealthStatus")));
} }
void EnemyBody::physics_process(double delta) { void EnemyBody::physics_process(double delta) {
GETSET(velocity, { GETSET(velocity, {
velocity = Vector3{ this->movement_direction.x * this->movement_speed, velocity.y, this->movement_direction.y * this->movement_speed } + (velocity * delta); velocity = Vector3{ this->movement_direction.x * this->movement_speed, velocity.y, this->movement_direction.y * this->movement_speed } + (velocity * delta);
if (!is_on_floor() && this->health->get_health() > 0) {
velocity += get_gravity() * delta;
}
}); });
if (!this->movement_direction.is_zero_approx()) { if (!this->movement_direction.is_zero_approx()) {
look_at(get_global_position() + Vector3{ this->movement_direction.x, 0.f, this->movement_direction.y }); look_at(get_global_position() + Vector3{ this->movement_direction.x, 0.f, this->movement_direction.y });

View file

@ -3,7 +3,7 @@
#include "core/io/resource.h" #include "core/io/resource.h"
#include "core/templates/hash_map.h" #include "core/templates/hash_map.h"
#include "scene/3d/node_3d.h" #include "scene/3d/marker_3d.h"
class MapRegion; class MapRegion;
class PatrolPath; class PatrolPath;
@ -19,8 +19,8 @@ public:
HashMap<int, Ref<PackedScene>> difficulty_spawns{}; HashMap<int, Ref<PackedScene>> difficulty_spawns{};
}; };
class EnemySpawner : public Node3D { class EnemySpawner : public Marker3D {
GDCLASS(EnemySpawner, Node3D); GDCLASS(EnemySpawner, Marker3D);
static void _bind_methods(); static void _bind_methods();
void on_phase_change(bool hunt); void on_phase_change(bool hunt);
void ready(); void ready();

View file

@ -1,4 +1,5 @@
#include "health_status.h" #include "health_status.h"
#include "core/config/engine.h"
#include "macros.h" #include "macros.h"
String HealthStatus::sig_death{ "death" }; String HealthStatus::sig_death{ "death" };

View file

@ -11,18 +11,28 @@ void NpcUnit::_bind_methods() {
BIND_HPROPERTY(Variant::OBJECT, region, PROPERTY_HINT_NODE_TYPE, "MapRegion"); BIND_HPROPERTY(Variant::OBJECT, region, PROPERTY_HINT_NODE_TYPE, "MapRegion");
} }
void NpcUnit::remove_npc(EnemyBody *npc) {
Transform3D const tf{ npc->get_global_transform() };
remove_child(npc);
this->get_parent()->add_child(npc);
npc->set_global_transform(tf);
}
void NpcUnit::on_npc_death() { void NpcUnit::on_npc_death() {
Vector<EnemyBody>::Size living{ this->npcs.size() };
// remove any dead npcs from the list // remove any dead npcs from the list
// leaving their bodies as separate nodes part of the tree // leaving their bodies as separate nodes part of the tree
for (EnemyBody *npc : this->npcs) { for (Vector<EnemyBody>::Size i{ 0 }; i < this->npcs.size(); ++i) {
EnemyBody *npc{ this->npcs[i] };
if (npc->get_health()->get_health() <= 0) { if (npc->get_health()->get_health() <= 0) {
this->get_parent()->add_child(npc); --living;
this->npcs.erase(npc);
break;
} }
} }
// remove unit from world once all npcs are dead // remove unit from world once all npcs are dead
if (this->npcs.size() <= 0) { if (living == 0) {
for (EnemyBody *npc : this->npcs) {
remove_npc(npc);
}
this->set_region(nullptr); // de-register from region this->set_region(nullptr); // de-register from region
this->queue_free(); this->queue_free();
} }

View file

@ -10,6 +10,7 @@ class MapRegion;
class NpcUnit : public Node3D { class NpcUnit : public Node3D {
GDCLASS(NpcUnit, Node3D); GDCLASS(NpcUnit, Node3D);
static void _bind_methods(); static void _bind_methods();
void remove_npc(EnemyBody *unit);
void on_npc_death(); void on_npc_death();
void on_npc_awareness_changed(bool seen); void on_npc_awareness_changed(bool seen);
void child_added(Node *node); void child_added(Node *node);

View file

@ -1,4 +1,6 @@
#include "player_input.h" #include "player_input.h"
#include "core/config/engine.h"
#include "core/input/input.h"
#include "scene/main/scene_tree.h" #include "scene/main/scene_tree.h"
String PlayerInput::sig_movement_input{ "movement_input" }; String PlayerInput::sig_movement_input{ "movement_input" };

View file

@ -1,4 +1,5 @@
#include "state_machine.h" #include "state_machine.h"
#include "core/config/engine.h"
#include "state.h" #include "state.h"
void StateMachine::_bind_methods() {} void StateMachine::_bind_methods() {}
@ -18,7 +19,7 @@ void StateMachine::switch_to_state(State *state) {
} }
void StateMachine::process(double delta) { void StateMachine::process(double delta) {
if (this->current_state) { if (this->current_state && is_enabled()) {
this->current_state->process(delta); this->current_state->process(delta);
String new_state{ this->current_state->get_next_state() }; String new_state{ this->current_state->get_next_state() };
if (new_state != this->current_state->get_class()) { if (new_state != this->current_state->get_class()) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,23 +7,43 @@ script/source = "extends EnemyWretched
@export var difficulty_weight : float = 10 @export var difficulty_weight : float = 10
func _on_health_status_death() -> void: var is_dead := false
func on_death() -> void:
%StateMachine.process_mode = Node.PROCESS_MODE_DISABLED %StateMachine.process_mode = Node.PROCESS_MODE_DISABLED
%NavigationAgent3D.process_mode = Node.PROCESS_MODE_DISABLED %NavigationAgent3D.process_mode = Node.PROCESS_MODE_DISABLED
$wretched/AnimationPlayer.play(\"death\")
$CollisionShape3D.disabled = true $CollisionShape3D.disabled = true
set_movement_direction(Vector2()) set_movement_direction(Vector2())
func _on_health_status_death() -> void:
$wretched/AnimationPlayer.play(\"death\")
get_unit().region.raise_difficulty(1.0 / difficulty_weight) get_unit().region.raise_difficulty(1.0 / difficulty_weight)
on_death.call_deferred()
is_dead = true
func _enter_tree() -> void:
if is_dead and $wretched/AnimationPlayer.current_animation != \"death\":
$wretched/AnimationPlayer.play.call_deferred(\"death\")
$wretched/AnimationPlayer.advance.call_deferred(INF)
elif is_dead:
$wretched/AnimationPlayer.play.call_deferred(\"death\")
" "
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ng1ul"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ng1ul"]
radius = 0.26953125 radius = 0.58691406
[node name="EnemyWretched" type="EnemyWretched"] [node name="EnemyWretched" type="EnemyWretched"]
wall_min_slide_angle = 0.0
script = SubResource("GDScript_qot2n") script = SubResource("GDScript_qot2n")
[node name="wretched" parent="." instance=ExtResource("1_qot2n")] [node name="wretched" parent="." instance=ExtResource("1_qot2n")]
[node name="Body" parent="wretched/Character/Skeleton3D" index="0"]
gi_mode = 0
[node name="club" parent="wretched/Character/Skeleton3D" index="1"]
gi_mode = 0
[node name="Hitbox" parent="wretched/Character/Skeleton3D" index="2" node_paths=PackedStringArray("health")] [node name="Hitbox" parent="wretched/Character/Skeleton3D" index="2" node_paths=PackedStringArray("health")]
health = NodePath("../../../../HealthStatus") health = NodePath("../../../../HealthStatus")

View file

@ -12,7 +12,7 @@ config_version=5
config/name="wave_survival" config/name="wave_survival"
run/main_scene="uid://dti7d1rslmhgp" run/main_scene="uid://dti7d1rslmhgp"
config/features=PackedStringArray("4.5", "Forward Plus") config/features=PackedStringArray("4.6", "Forward Plus")
config/icon="res://icon.svg" config/icon="res://icon.svg"
[display] [display]
@ -103,13 +103,10 @@ reload={
3d_physics/layer_4="Hitbox(Player)" 3d_physics/layer_4="Hitbox(Player)"
3d_physics/layer_5="Interactables" 3d_physics/layer_5="Interactables"
[physics]
3d/physics_engine="Jolt Physics"
[rendering] [rendering]
lights_and_shadows/directional_shadow/size=8192 lights_and_shadows/directional_shadow/size=8192
lights_and_shadows/directional_shadow/soft_shadow_filter_quality=4 lights_and_shadows/directional_shadow/soft_shadow_filter_quality=4
lights_and_shadows/positional_shadow/soft_shadow_filter_quality=5 lights_and_shadows/positional_shadow/soft_shadow_filter_quality=5
global_illumination/voxel_gi/quality=1
lights_and_shadows/positional_shadow/atlas_size=8192 lights_and_shadows/positional_shadow/atlas_size=8192