feat: added interactions, demo packs and destructables
This commit is contained in:
parent
d66c999039
commit
5a4ac26c72
33
modules/wave_survival/interactable.cpp
Normal file
33
modules/wave_survival/interactable.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "interactable.h"
|
||||
#include "macros.h"
|
||||
|
||||
void Interactable::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("activate", "interactor"), &self_type::activate);
|
||||
ClassDB::bind_method(D_METHOD("set_highlighted", "value"), &self_type::set_highlighted);
|
||||
ClassDB::bind_method(D_METHOD("get_highlighted"), &self_type::get_highlighted);
|
||||
GDVIRTUAL_BIND(_activated, "interactor");
|
||||
GDVIRTUAL_BIND(_highlight_changed, "interactor", "value");
|
||||
}
|
||||
|
||||
void Interactable::activate(PlayerInteractor *interactor) {
|
||||
activated(interactor);
|
||||
GDVIRTUAL_CALL(_activated, interactor);
|
||||
}
|
||||
|
||||
void Interactable::set_highlighted(PlayerInteractor *interactor, bool value) {
|
||||
this->highlighted = value;
|
||||
highlight_changed(interactor, value);
|
||||
GDVIRTUAL_CALL(_highlight_changed, interactor, value);
|
||||
}
|
||||
|
||||
bool Interactable::get_highlighted() const {
|
||||
return this->highlighted;
|
||||
}
|
||||
|
||||
void Interactable::set_highlight_tooltip(String value) {
|
||||
this->highlight_tooltip = value;
|
||||
}
|
||||
|
||||
String Interactable::get_highlight_tooltip() const {
|
||||
return this->highlight_tooltip;
|
||||
}
|
30
modules/wave_survival/interactable.h
Normal file
30
modules/wave_survival/interactable.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef INTERACTABLE_H
|
||||
#define INTERACTABLE_H
|
||||
|
||||
#include "player_interactor.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Interactable : public Node {
|
||||
GDCLASS(Interactable, Node);
|
||||
static void _bind_methods();
|
||||
|
||||
protected:
|
||||
virtual void activated(PlayerInteractor *interactor) {}
|
||||
GDVIRTUAL1_REQUIRED(_activated, PlayerInteractor *);
|
||||
virtual void highlight_changed(PlayerInteractor *interactor, bool new_value) {}
|
||||
GDVIRTUAL2(_highlight_changed, PlayerInteractor *, bool);
|
||||
|
||||
public:
|
||||
void activate(PlayerInteractor *interactor);
|
||||
|
||||
void set_highlighted(PlayerInteractor *interactor, bool value);
|
||||
bool get_highlighted() const;
|
||||
void set_highlight_tooltip(String value);
|
||||
String get_highlight_tooltip() const;
|
||||
|
||||
private:
|
||||
bool highlighted{ false };
|
||||
String highlight_tooltip{ "Activate" };
|
||||
};
|
||||
|
||||
#endif // !INTERACTABLE_H
|
|
@ -9,6 +9,7 @@ String PlayerInput::sig_switch_weapon{ "switch_weapon" };
|
|||
String PlayerInput::sig_run{ "run" };
|
||||
String PlayerInput::sig_jump{ "jump" };
|
||||
String PlayerInput::sig_crouch{ "crouch" };
|
||||
String PlayerInput::sig_activate{ "activate" };
|
||||
|
||||
void PlayerInput::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo(sig_movement_input, PropertyInfo(Variant::VECTOR2, "axes")));
|
||||
|
@ -19,6 +20,7 @@ void PlayerInput::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo(sig_run, PropertyInfo(Variant::BOOL, "is_running")));
|
||||
ADD_SIGNAL(MethodInfo(sig_jump));
|
||||
ADD_SIGNAL(MethodInfo(sig_crouch, PropertyInfo(Variant::BOOL, "is_crouching")));
|
||||
ADD_SIGNAL(MethodInfo(sig_activate));
|
||||
}
|
||||
|
||||
void PlayerInput::normalize_input() {
|
||||
|
@ -83,4 +85,7 @@ void PlayerInput::unhandled_input(Ref<InputEvent> const &event) {
|
|||
if (event->is_action("switch_weapon") && event->is_pressed()) {
|
||||
emit_signal(sig_switch_weapon);
|
||||
}
|
||||
if (event->is_action("activate") && event->is_pressed()) {
|
||||
emit_signal(sig_activate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
static String sig_run;
|
||||
static String sig_jump;
|
||||
static String sig_crouch;
|
||||
static String sig_activate;
|
||||
|
||||
private:
|
||||
bool was_paused{ false };
|
||||
|
|
87
modules/wave_survival/player_interactor.cpp
Normal file
87
modules/wave_survival/player_interactor.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "player_interactor.h"
|
||||
#include "interactable.h"
|
||||
#include "player_body.h"
|
||||
#include "player_input.h"
|
||||
|
||||
void PlayerInteractor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("pickup_demo_pack"), &self_type::pickup_demo_pack);
|
||||
ClassDB::bind_method(D_METHOD("try_use_demo_pack"), &self_type::try_use_demo_pack);
|
||||
}
|
||||
|
||||
void PlayerInteractor::highlight_removed() {
|
||||
this->interactable->disconnect("tree_exiting", this->on_highlight_removed);
|
||||
this->interactable->set_highlighted(this, false);
|
||||
this->interactable = nullptr;
|
||||
}
|
||||
|
||||
void PlayerInteractor::activate() {
|
||||
if (interactable != nullptr) {
|
||||
interactable->activate(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerInteractor::ready() {
|
||||
PlayerInput *input{ cast_to<PlayerInput>(get_node(NodePath("%PlayerInput"))) };
|
||||
input->connect(PlayerInput::sig_activate, callable_mp(this, &self_type::activate));
|
||||
}
|
||||
|
||||
void PlayerInteractor::process(double delta) {
|
||||
Interactable *new_interactable{ nullptr };
|
||||
if (is_colliding()) {
|
||||
if (Node * hit{ cast_to<Node>(this->get_collider(0)) }) {
|
||||
TypedArray<Node> interactables = hit->find_children("*", Interactable::get_class_static());
|
||||
new_interactable = interactables.size() > 0 ? cast_to<Interactable>(interactables[0]) : nullptr;
|
||||
}
|
||||
}
|
||||
if (new_interactable != this->interactable) {
|
||||
if (this->interactable != nullptr) {
|
||||
this->interactable->set_highlighted(this, false);
|
||||
this->interactable->disconnect("tree_exiting", this->on_highlight_removed);
|
||||
}
|
||||
this->interactable = new_interactable;
|
||||
if (this->interactable != nullptr) {
|
||||
this->interactable->set_highlighted(this, true);
|
||||
this->interactable->connect("tree_exiting", this->on_highlight_removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerInteractor::_notification(int what) {
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
switch (what) {
|
||||
default:
|
||||
return;
|
||||
case NOTIFICATION_READY:
|
||||
set_process(true);
|
||||
ready();
|
||||
return;
|
||||
case NOTIFICATION_PROCESS:
|
||||
process(get_process_delta_time());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray PlayerInteractor::get_configuration_warnings() const {
|
||||
PackedStringArray warnings{ super_type::get_configuration_warnings() };
|
||||
if (cast_to<PlayerBody>(get_owner()) == nullptr) {
|
||||
warnings.push_back("PlayerInteractor should be part of and owned by a PlayerBody node to work properly.");
|
||||
}
|
||||
if (get_node(NodePath("%PlayerInput")) == nullptr) {
|
||||
warnings.push_back("PlayerInteractor should have a node named '%PlayerInput' in the same scene as it");
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void PlayerInteractor::pickup_demo_pack() {
|
||||
++this->num_demo_packs;
|
||||
}
|
||||
|
||||
bool PlayerInteractor::try_use_demo_pack() {
|
||||
if (this->num_demo_packs > 0) {
|
||||
--this->num_demo_packs;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
30
modules/wave_survival/player_interactor.h
Normal file
30
modules/wave_survival/player_interactor.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef PLAYER_INTERACTOR_H
|
||||
#define PLAYER_INTERACTOR_H
|
||||
|
||||
#include "scene/3d/physics/shape_cast_3d.h"
|
||||
class Interactable;
|
||||
|
||||
class PlayerInteractor : public ShapeCast3D {
|
||||
GDCLASS(PlayerInteractor, ShapeCast3D);
|
||||
static void _bind_methods();
|
||||
void highlight_removed();
|
||||
void activate();
|
||||
void ready();
|
||||
void process(double delta);
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
|
||||
public:
|
||||
virtual PackedStringArray get_configuration_warnings() const override;
|
||||
void pickup_demo_pack();
|
||||
bool try_use_demo_pack();
|
||||
|
||||
private:
|
||||
int num_demo_packs{ 0 };
|
||||
Interactable *interactable{ nullptr };
|
||||
Callable on_highlight_removed{ callable_mp(this, &self_type::highlight_removed) };
|
||||
static String activate_method_name;
|
||||
};
|
||||
|
||||
#endif // !PLAYER_INTERACTOR_H
|
|
@ -7,12 +7,14 @@
|
|||
#include "wave_survival/health_status.h"
|
||||
#include "wave_survival/hitbox.h"
|
||||
#include "wave_survival/hitscan_muzzle.h"
|
||||
#include "wave_survival/interactable.h"
|
||||
#include "wave_survival/npc_unit.h"
|
||||
#include "wave_survival/patrol_path.h"
|
||||
#include "wave_survival/player_body.h"
|
||||
#include "wave_survival/player_camera.h"
|
||||
#include "wave_survival/player_detector.h"
|
||||
#include "wave_survival/player_input.h"
|
||||
#include "wave_survival/player_interactor.h"
|
||||
#include "wave_survival/state.h"
|
||||
#include "wave_survival/state_machine.h"
|
||||
#include "wave_survival/weapon_base.h"
|
||||
|
@ -43,6 +45,8 @@ void initialize_wave_survival_module(ModuleInitializationLevel p_level) {
|
|||
GDREGISTER_CLASS(PlayerDetector);
|
||||
GDREGISTER_CLASS(Hitbox);
|
||||
GDREGISTER_CLASS(DamageBox);
|
||||
GDREGISTER_CLASS(PlayerInteractor);
|
||||
GDREGISTER_CLASS(Interactable);
|
||||
}
|
||||
|
||||
void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) {
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
void set_starting_weapon(Ref<PackedScene> weapon_scene);
|
||||
Ref<PackedScene> get_starting_weapon() const;
|
||||
|
||||
void pickup_demo_pack();
|
||||
bool try_use_demo_pack();
|
||||
|
||||
private:
|
||||
Node3D *weapon_parent{ nullptr };
|
||||
unsigned current{ 0 };
|
||||
|
|
File diff suppressed because one or more lines are too long
27
project/objects/pickups/demo_pack_pickup.tscn
Normal file
27
project/objects/pickups/demo_pack_pickup.tscn
Normal file
|
@ -0,0 +1,27 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://cclghy01gblif"]
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_kl827"]
|
||||
radius = 0.1282442
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_kl827"]
|
||||
script/source = "extends Interactable
|
||||
|
||||
func _activated(interactor: PlayerInteractor) -> void:
|
||||
interactor.pickup_demo_pack()
|
||||
get_owner().queue_free()
|
||||
"
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_kl827"]
|
||||
size = Vector3(0.2, 0.1, 0.2)
|
||||
|
||||
[node name="DemoPackPickup" type="StaticBody3D"]
|
||||
collision_layer = 18
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("SphereShape3D_kl827")
|
||||
|
||||
[node name="Interactable" type="Interactable" parent="."]
|
||||
script = SubResource("GDScript_kl827")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
mesh = SubResource("BoxMesh_kl827")
|
|
@ -1,7 +1,10 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://snjgu4yp5swd"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://snjgu4yp5swd"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://ce40pq785yoyi" path="res://objects/weapons/rifle.tscn" id="1_eqqp1"]
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_eqqp1"]
|
||||
radius = 0.2
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_eqqp1"]
|
||||
script/source = "extends HealthStatus
|
||||
|
||||
|
@ -23,6 +26,12 @@ unique_name_in_owner = true
|
|||
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 1.60811, 0)
|
||||
fov = 60.0
|
||||
|
||||
[node name="PlayerInteractor" type="PlayerInteractor" parent="PlayerCamera"]
|
||||
shape = SubResource("SphereShape3D_eqqp1")
|
||||
target_position = Vector3(0, 0, -2)
|
||||
collision_mask = 22
|
||||
collide_with_areas = true
|
||||
|
||||
[node name="PlayerInput" type="PlayerInput" parent="."]
|
||||
unique_name_in_owner = true
|
||||
process_mode = 3
|
||||
|
|
|
@ -91,6 +91,7 @@ activate={
|
|||
3d_physics/layer_2="Precision"
|
||||
3d_physics/layer_3="Hitbox(Enemy)"
|
||||
3d_physics/layer_4="Hitbox(Player)"
|
||||
3d_physics/layer_5="Interactables"
|
||||
|
||||
[physics]
|
||||
|
||||
|
|
Loading…
Reference in a new issue