From ee14efe22e183e1f0965d6789e3863a7a02f224f Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 9 Nov 2019 12:15:45 +0100 Subject: [PATCH] Implemented better script handling. --- src/Battling/Flow/TurnHandler.cpp | 9 +-- src/Battling/Models/Creature.hpp | 10 +-- src/Battling/ScriptHandling/Script.hpp | 8 ++- .../ScriptHandling/ScriptAggregator.hpp | 66 +++++++++++++++++++ src/Battling/ScriptHandling/ScriptMacros.cpp | 14 ++++ src/Battling/ScriptHandling/ScriptSet.hpp | 14 ++-- src/Battling/ScriptHandling/ScriptSource.hpp | 15 +++++ 7 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 src/Battling/ScriptHandling/ScriptAggregator.hpp create mode 100644 src/Battling/ScriptHandling/ScriptMacros.cpp create mode 100644 src/Battling/ScriptHandling/ScriptSource.hpp diff --git a/src/Battling/Flow/TurnHandler.cpp b/src/Battling/Flow/TurnHandler.cpp index 81e8053..e81a4a3 100644 --- a/src/Battling/Flow/TurnHandler.cpp +++ b/src/Battling/Flow/TurnHandler.cpp @@ -1,6 +1,7 @@ #include "TurnHandler.hpp" #include "../Models/Battle.hpp" #include "../../Core/Exceptions/NotImplementedException.hpp" +#include "../ScriptHandling/ScriptMacros.cpp" using namespace CreatureLib::Battling; @@ -78,15 +79,15 @@ void TurnHandler::ExecuteAttackChoice(const AttackTurnChoice *choice) { void TurnHandler::HandleAttackForTarget(ExecutingAttack &attack, Creature *target, ExecutingAttack::TargetData &targetData) { //HOOK: Check if attack fails on target - target->ExecuteScripts(Hook::IncomingAttackFails, {std::any(attack), std::any(target)}); + std::array sources = {target}; + HOOK(OnIncomingAttackFails, sources, &attack, target); //HOOK: Check if target is invulnerable - target->ExecuteScripts(Hook::IsInvulnerable, {std::any(attack), std::any(target)}); - + HOOK(IsInvulnerable, sources, &attack, target); if (!targetData.IsHit()){ //HOOK: On attack miss. - target->ExecuteScripts(Hook::AttackMiss, {std::any(attack), std::any(target)}); + HOOK(OnAttackMiss, sources, &attack, target); return; } diff --git a/src/Battling/Models/Creature.hpp b/src/Battling/Models/Creature.hpp index 031497b..2e4238d 100644 --- a/src/Battling/Models/Creature.hpp +++ b/src/Battling/Models/Creature.hpp @@ -7,6 +7,8 @@ #include "LearnedAttack.hpp" #include "DamageSource.hpp" #include "../ScriptHandling/ScriptSet.hpp" +#include "../ScriptHandling/ScriptAggregator.hpp" +#include "../ScriptHandling/ScriptSource.hpp" namespace CreatureLib::Battling{ // Forward declare battle class @@ -14,7 +16,7 @@ namespace CreatureLib::Battling{ class BattleSide; class BattleLibrary; - class Creature { + class Creature : public ScriptSource{ GetProperty(const Library::CreatureSpecies*, Species); GetProperty(const Library::SpeciesVariant*, Variant); GetProperty(uint8_t, Level); @@ -63,9 +65,9 @@ namespace CreatureLib::Battling{ [[nodiscard]] const std::vector& GetTypes() const; [[nodiscard]] bool HasType(uint8_t type) const; - void ExecuteScripts(Hook hook, const std::vector& args){ - _status->Execute(hook, args); - _volatile.Execute(hook, args); + void GetActiveScripts(ScriptAggregator& aggr) override{ + aggr.Add(_status); + aggr.Add(&_volatile); } //region Stat APIs diff --git a/src/Battling/ScriptHandling/Script.hpp b/src/Battling/ScriptHandling/Script.hpp index d6460e2..8d14f9e 100644 --- a/src/Battling/ScriptHandling/Script.hpp +++ b/src/Battling/ScriptHandling/Script.hpp @@ -8,6 +8,9 @@ #include "Hooks.hpp" namespace CreatureLib::Battling{ + class ExecutingAttack; + class Creature; + class Script{ const std::string _name; @@ -15,12 +18,15 @@ namespace CreatureLib::Battling{ explicit Script(std::string name) :_name(std::move(name)){} virtual ~Script() = default; - virtual void Execute(Hook hook, const std::vector& args){}; virtual void Stack(){}; const std::string& GetName(){ return _name; } + + virtual void OnIncomingAttackFails(ExecutingAttack* attack, Creature* target){}; + virtual void IsInvulnerable(ExecutingAttack* attack, Creature* target){}; + virtual void OnAttackMiss(ExecutingAttack* attack, Creature* target){}; }; } diff --git a/src/Battling/ScriptHandling/ScriptAggregator.hpp b/src/Battling/ScriptHandling/ScriptAggregator.hpp new file mode 100644 index 0000000..48036f6 --- /dev/null +++ b/src/Battling/ScriptHandling/ScriptAggregator.hpp @@ -0,0 +1,66 @@ +#ifndef CREATURELIB_SCRIPTAGGREGATOR_HPP +#define CREATURELIB_SCRIPTAGGREGATOR_HPP + +#include +#include +#include "Script.hpp" +#include "ScriptSet.hpp" + +namespace CreatureLib::Battling{ + class ScriptAggregator{ + std::queue _queue; + bool _isSetSet = false; + std::__detail::_Node_iterator, false, true> _setIterator; + std::__detail::_Node_iterator, false, true> _setEnd; + public: + ScriptAggregator() = default; + + void Add(Script* script){ + _queue.push(script); + } + + void Add(ScriptSet* scriptSet){ + _queue.push(scriptSet); + } + + bool HasNext(){ + return !_queue.empty() || _isSetSet; + } + + Script* GetNext(){ + // We can probably do this in a cleaner version once C++ 20 drops with Coroutine support. + if (_isSetSet){ + if (_setIterator == _setEnd){ + _isSetSet = false; + return GetNext(); + } + auto s = _setIterator->second; + _setIterator.operator++(); + if (_setIterator == _setEnd){ + _isSetSet = false; + } + return s; + } + if (_queue.empty()) + return nullptr; + auto next = _queue.front(); + _queue.pop(); + if (next.type() == typeid(Script*)){ + return std::any_cast(next); + } + else{ + auto set = std::any_cast(next); + if (set->Count() == 0) + return GetNext(); + auto it = set->GetIterator(); + _setIterator = it->begin(); + _setEnd = it->end(); + _isSetSet = true; + return GetNext(); + } + throw NotReachableException(); + } + }; +} + +#endif //CREATURELIB_SCRIPTAGGREGATOR_HPP diff --git a/src/Battling/ScriptHandling/ScriptMacros.cpp b/src/Battling/ScriptHandling/ScriptMacros.cpp new file mode 100644 index 0000000..bba13cc --- /dev/null +++ b/src/Battling/ScriptHandling/ScriptMacros.cpp @@ -0,0 +1,14 @@ +#define HOOK(hookName, sources, ... ) \ +{ \ +auto aggregator = CreatureLib::Battling::ScriptAggregator(); \ +for (auto source: sources){ \ + source -> GetActiveScripts(aggregator); \ +} \ +while (aggregator.HasNext()){ \ +auto next = aggregator.GetNext(); \ +if (next == nullptr) continue; \ +next->hookName(__VA_ARGS__); \ +} \ +} + + diff --git a/src/Battling/ScriptHandling/ScriptSet.hpp b/src/Battling/ScriptHandling/ScriptSet.hpp index 8e9a774..7d900aa 100644 --- a/src/Battling/ScriptHandling/ScriptSet.hpp +++ b/src/Battling/ScriptHandling/ScriptSet.hpp @@ -9,12 +9,6 @@ namespace CreatureLib::Battling{ class ScriptSet{ std::unordered_map _scripts; public: - void Execute(Hook hook, const std::vector& args){ - for (auto s: _scripts){ - s.second->Execute(hook, args); - } - } - void Add(Script* script){ auto f = _scripts.find(script->GetName()); if (f != _scripts.end()){ @@ -28,6 +22,14 @@ namespace CreatureLib::Battling{ void Remove(const std::string& key){ _scripts.erase(key); } + + size_t Count() const{ + return _scripts.size(); + } + + std::unordered_map * GetIterator(){ + return &_scripts; + } }; } diff --git a/src/Battling/ScriptHandling/ScriptSource.hpp b/src/Battling/ScriptHandling/ScriptSource.hpp new file mode 100644 index 0000000..ccc52dd --- /dev/null +++ b/src/Battling/ScriptHandling/ScriptSource.hpp @@ -0,0 +1,15 @@ + + +#ifndef CREATURELIB_SCRIPTSOURCE_HPP +#define CREATURELIB_SCRIPTSOURCE_HPP + +#include "ScriptAggregator.hpp" + +namespace CreatureLib::Battling{ + class ScriptSource { + public: + virtual void GetActiveScripts(ScriptAggregator& aggr) = 0; + }; +} + +#endif //CREATURELIB_SCRIPTSOURCE_HPP