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
__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
# 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
# Running Editor

View file

@ -33,9 +33,11 @@ void DamageBox::_notification(int what) {
default:
return;
case NOTIFICATION_ENTER_TREE:
connect("body_entered", callable_mp(this, &self_type::on_body_entered));
connect("area_entered", callable_mp(this, &self_type::on_body_entered));
set_monitoring(false);
if (!is_ready()) {
connect("body_entered", callable_mp(this, &self_type::on_body_entered));
connect("area_entered", callable_mp(this, &self_type::on_body_entered));
set_monitoring(false);
}
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
#include "wave_survival/enemy_body.h"
#include "wave_survival/state.h"
class AnimationPlayer;
class NavigationAgent3D;
class EnemyRifleman : public EnemyBody {
GDCLASS(EnemyRifleman, EnemyBody);
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

View file

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

View file

@ -19,11 +19,15 @@ void EnemyBody::on_child_added(Node *node) {
void EnemyBody::ready() {
this->fsm = cast_to<StateMachine>(get_node(NodePath("%StateMachine")));
this->nav = cast_to<NavigationAgent3D>(get_node(NodePath("%NavigationAgent3D")));
this->health = cast_to<HealthStatus>(get_node(NodePath("%HealthStatus")));
}
void EnemyBody::physics_process(double delta) {
GETSET(velocity, {
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()) {
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/templates/hash_map.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/marker_3d.h"
class MapRegion;
class PatrolPath;
@ -19,8 +19,8 @@ public:
HashMap<int, Ref<PackedScene>> difficulty_spawns{};
};
class EnemySpawner : public Node3D {
GDCLASS(EnemySpawner, Node3D);
class EnemySpawner : public Marker3D {
GDCLASS(EnemySpawner, Marker3D);
static void _bind_methods();
void on_phase_change(bool hunt);
void ready();

View file

@ -1,4 +1,5 @@
#include "health_status.h"
#include "core/config/engine.h"
#include "macros.h"
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");
}
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() {
Vector<EnemyBody>::Size living{ this->npcs.size() };
// remove any dead npcs from the list
// 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) {
this->get_parent()->add_child(npc);
this->npcs.erase(npc);
break;
--living;
}
}
// 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->queue_free();
}

View file

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

View file

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

View file

@ -1,4 +1,5 @@
#include "state_machine.h"
#include "core/config/engine.h"
#include "state.h"
void StateMachine::_bind_methods() {}
@ -18,7 +19,7 @@ void StateMachine::switch_to_state(State *state) {
}
void StateMachine::process(double delta) {
if (this->current_state) {
if (this->current_state && is_enabled()) {
this->current_state->process(delta);
String new_state{ this->current_state->get_next_state() };
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
func _on_health_status_death() -> void:
var is_dead := false
func on_death() -> void:
%StateMachine.process_mode = Node.PROCESS_MODE_DISABLED
%NavigationAgent3D.process_mode = Node.PROCESS_MODE_DISABLED
$wretched/AnimationPlayer.play(\"death\")
$CollisionShape3D.disabled = true
set_movement_direction(Vector2())
func _on_health_status_death() -> void:
$wretched/AnimationPlayer.play(\"death\")
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"]
radius = 0.26953125
radius = 0.58691406
[node name="EnemyWretched" type="EnemyWretched"]
wall_min_slide_angle = 0.0
script = SubResource("GDScript_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")]
health = NodePath("../../../../HealthStatus")

View file

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