Skip to content

Commit

Permalink
Add TryActivateAbilityEffect class (resolves #8)
Browse files Browse the repository at this point in the history
This commit also modifies the behavior of `Effect` to only call `Effect.start` right before ticking. This is helpful for sequential mode, because previously all effects would be started upon instantiation, which is unintuitively non-sequential.
  • Loading branch information
audse committed Jan 23, 2024
1 parent a62d070 commit 247d713
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ This is where the bulk of your custom game logic will go. It has access to the `
* `AttributeEffect` - modifies an `Attribute`
* `TagEffect` - adds or removes `Tag`s from `AbilitySystem`
* `WaitEffect` - arbitrarily delays `AbilityEvent`
* `TryActivateAbilityEffect` - attempts to activate an `Ability` on the `AbilitySystem`

### `Tag`

Expand Down
Binary file not shown.
Binary file not shown.
19 changes: 14 additions & 5 deletions src/ability_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ void AbilityEvent::_bind_methods() {
void AbilityEvent::start(AbilitySystem *owner) {
status = Status::READY;
effect_instances.clear();
for_each(this->ability->get_effects(), [this, owner](Ref<Effect> effect) {
Ref<Effect> instance = effect->duplicate(true);
for_each_i(this->ability->get_effects(), [this, owner](Ref<Effect> effect, int i) {
Ref<Effect> instance = effect->duplicate(false);
effect_instances.append(instance);
instance->start(owner);
// Start the first effect (when in sequential mode) or all effects (when in parallel mode)
if (i == 0 || ability->effect_mode == EffectMode::PARALLEL)
instance->start(owner);
});
status = Status::RUNNING;
}
Expand Down Expand Up @@ -70,17 +72,24 @@ void AbilityEvent::tick_parallel(AbilitySystem *owner, float delta) {
}

void AbilityEvent::tick_sequential(AbilitySystem *owner, float delta) {
Status current_status = Status::READY;
// Tick the first effect, if it exists.
if (effect_instances.size()) {
Ref<Effect> effect = effect_instances[0];
Status effect_status = effect->tick(owner, delta);
current_status = effect->tick(owner, delta);
// If the first effect is finished, remove it from the list.
if (effect_status == Status::FINISHED) {
if (current_status == Status::FINISHED) {
effect_instances.pop_front();
emit_signal(as_signal::EffectFinished, effect);
owner->emit_signal(as_signal::EffectsChanged);
}
}

// Start the next effect, if it exists and is not already running.
if (current_status != Status::RUNNING && effect_instances.size()) {
Ref<Effect> effect = effect_instances[0];
effect->start(owner);
}
}

String AbilityEvent::_to_string() const {
Expand Down
4 changes: 2 additions & 2 deletions src/effect/attribute_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ int AttributeEffect::_tick(AbilitySystem *owner, float delta) {
float effect = RANDF_RANGE(min_effect, max_effect);
owner->modify_attribute_value(attribute, effect);
} else {
print_error("Searching for attribute: " + stringify_variants(attribute));
print_error("Owner missing attribute! (attributes are " + stringify_variants(owner->get_attribute_dict()) + ")");
print_error("Searching for attribute: " + stringify(attribute));
print_error("Owner missing attribute! (attributes are " + stringify(owner->get_attribute_dict()) + ")");
}
return Status::FINISHED;
}
30 changes: 30 additions & 0 deletions src/effect/try_activate_ability_effect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "try_activate_ability_effect.h"

#include <godot_cpp/variant/utility_functions.hpp>

#include "../ability_system.h"

void TryActivateAbilityEffect::_bind_methods() {
BIND_GETSET(TryActivateAbilityEffect, ability);

OBJECT_PROP(Ability, ability);
}

void TryActivateAbilityEffect::_start(AbilitySystem *owner) {
if (ability.is_null()) {
print_error("No ability provided!");
return;
}

if (!owner->has_ability(ability)) {
String message = fmt(
"Owner is missing ability {0}! Has abilities: {1}",
ability->to_string(),
stringify(owner->get_abilities())
);
print_error(message);
} else if (!owner->can_activate(ability))
print_error(fmt("Owner cannot activate ability {0}!", ability->to_string()));

owner->activate(ability);
}
25 changes: 25 additions & 0 deletions src/effect/try_activate_ability_effect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef AS_TRYACTIVATEABILITYEFFECT_HPP
#define AS_TRYACTIVATEABILITYEFFECT_HPP

#include "../ability.hpp"
#include "effect.h"

class TryActivateAbilityEffect : public Effect {
GDCLASS(TryActivateAbilityEffect, Effect);

private:
Ref<Ability> ability;

protected:
static void _bind_methods();

public:
GETSET_RESOURCE(Ref<Ability>, ability)

virtual void _start(AbilitySystem *owner) override;
virtual int _tick(AbilitySystem *owner, float delta) override {
return Status::FINISHED;
}
};

#endif
2 changes: 2 additions & 0 deletions src/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "effect/attribute_effect.h"
#include "effect/tag_effect.h"
#include "effect/wait_effect.hpp"
#include "effect/try_activate_ability_effect.h"

// Editor
#include "editor/viewer_base.h"
Expand Down Expand Up @@ -45,6 +46,7 @@ void initialize_ability_system_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<WaitEffect>();
ClassDB::register_class<AttributeEffect>();
ClassDB::register_class<TagEffect>();
ClassDB::register_class<TryActivateAbilityEffect>();
ClassDB::register_class<AbilitySystem>();

// Editor
Expand Down

0 comments on commit 247d713

Please sign in to comment.