feat: added interactions, demo packs and destructables

This commit is contained in:
Sara 2025-07-28 21:17:11 +02:00
parent d66c999039
commit 5a4ac26c72
12 changed files with 369 additions and 23 deletions

View 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;
}

View 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

View file

@ -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);
}
}

View file

@ -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 };

View 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;
}

View 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

View file

@ -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) {

View file

@ -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 };