Implemented better script handling.

This commit is contained in:
Deukhoofd 2019-11-09 12:15:45 +01:00
parent c3bfbb569e
commit ee14efe22e
7 changed files with 121 additions and 15 deletions

View File

@ -1,6 +1,7 @@
#include "TurnHandler.hpp" #include "TurnHandler.hpp"
#include "../Models/Battle.hpp" #include "../Models/Battle.hpp"
#include "../../Core/Exceptions/NotImplementedException.hpp" #include "../../Core/Exceptions/NotImplementedException.hpp"
#include "../ScriptHandling/ScriptMacros.cpp"
using namespace CreatureLib::Battling; using namespace CreatureLib::Battling;
@ -78,15 +79,15 @@ void TurnHandler::ExecuteAttackChoice(const AttackTurnChoice *choice) {
void TurnHandler::HandleAttackForTarget(ExecutingAttack &attack, Creature *target, ExecutingAttack::TargetData &targetData) { void TurnHandler::HandleAttackForTarget(ExecutingAttack &attack, Creature *target, ExecutingAttack::TargetData &targetData) {
//HOOK: Check if attack fails on target //HOOK: Check if attack fails on target
target->ExecuteScripts(Hook::IncomingAttackFails, {std::any(attack), std::any(target)}); std::array<ScriptSource*, 1> sources = {target};
HOOK(OnIncomingAttackFails, sources, &attack, target);
//HOOK: Check if target is invulnerable //HOOK: Check if target is invulnerable
target->ExecuteScripts(Hook::IsInvulnerable, {std::any(attack), std::any(target)}); HOOK(IsInvulnerable, sources, &attack, target);
if (!targetData.IsHit()){ if (!targetData.IsHit()){
//HOOK: On attack miss. //HOOK: On attack miss.
target->ExecuteScripts(Hook::AttackMiss, {std::any(attack), std::any(target)}); HOOK(OnAttackMiss, sources, &attack, target);
return; return;
} }

View File

@ -7,6 +7,8 @@
#include "LearnedAttack.hpp" #include "LearnedAttack.hpp"
#include "DamageSource.hpp" #include "DamageSource.hpp"
#include "../ScriptHandling/ScriptSet.hpp" #include "../ScriptHandling/ScriptSet.hpp"
#include "../ScriptHandling/ScriptAggregator.hpp"
#include "../ScriptHandling/ScriptSource.hpp"
namespace CreatureLib::Battling{ namespace CreatureLib::Battling{
// Forward declare battle class // Forward declare battle class
@ -14,7 +16,7 @@ namespace CreatureLib::Battling{
class BattleSide; class BattleSide;
class BattleLibrary; class BattleLibrary;
class Creature { class Creature : public ScriptSource{
GetProperty(const Library::CreatureSpecies*, Species); GetProperty(const Library::CreatureSpecies*, Species);
GetProperty(const Library::SpeciesVariant*, Variant); GetProperty(const Library::SpeciesVariant*, Variant);
GetProperty(uint8_t, Level); GetProperty(uint8_t, Level);
@ -63,9 +65,9 @@ namespace CreatureLib::Battling{
[[nodiscard]] const std::vector<uint8_t>& GetTypes() const; [[nodiscard]] const std::vector<uint8_t>& GetTypes() const;
[[nodiscard]] bool HasType(uint8_t type) const; [[nodiscard]] bool HasType(uint8_t type) const;
void ExecuteScripts(Hook hook, const std::vector<std::any>& args){ void GetActiveScripts(ScriptAggregator& aggr) override{
_status->Execute(hook, args); aggr.Add(_status);
_volatile.Execute(hook, args); aggr.Add(&_volatile);
} }
//region Stat APIs //region Stat APIs

View File

@ -8,6 +8,9 @@
#include "Hooks.hpp" #include "Hooks.hpp"
namespace CreatureLib::Battling{ namespace CreatureLib::Battling{
class ExecutingAttack;
class Creature;
class Script{ class Script{
const std::string _name; const std::string _name;
@ -15,12 +18,15 @@ namespace CreatureLib::Battling{
explicit Script(std::string name) :_name(std::move(name)){} explicit Script(std::string name) :_name(std::move(name)){}
virtual ~Script() = default; virtual ~Script() = default;
virtual void Execute(Hook hook, const std::vector<std::any>& args){};
virtual void Stack(){}; virtual void Stack(){};
const std::string& GetName(){ const std::string& GetName(){
return _name; return _name;
} }
virtual void OnIncomingAttackFails(ExecutingAttack* attack, Creature* target){};
virtual void IsInvulnerable(ExecutingAttack* attack, Creature* target){};
virtual void OnAttackMiss(ExecutingAttack* attack, Creature* target){};
}; };
} }

View File

@ -0,0 +1,66 @@
#ifndef CREATURELIB_SCRIPTAGGREGATOR_HPP
#define CREATURELIB_SCRIPTAGGREGATOR_HPP
#include <any>
#include <queue>
#include "Script.hpp"
#include "ScriptSet.hpp"
namespace CreatureLib::Battling{
class ScriptAggregator{
std::queue<std::any> _queue;
bool _isSetSet = false;
std::__detail::_Node_iterator<std::pair<const std::string, Script *>, false, true> _setIterator;
std::__detail::_Node_iterator<std::pair<const std::string, Script *>, 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<Script*>(next);
}
else{
auto set = std::any_cast<ScriptSet*>(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

View File

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

View File

@ -9,12 +9,6 @@ namespace CreatureLib::Battling{
class ScriptSet{ class ScriptSet{
std::unordered_map<std::string, Script*> _scripts; std::unordered_map<std::string, Script*> _scripts;
public: public:
void Execute(Hook hook, const std::vector<std::any>& args){
for (auto s: _scripts){
s.second->Execute(hook, args);
}
}
void Add(Script* script){ void Add(Script* script){
auto f = _scripts.find(script->GetName()); auto f = _scripts.find(script->GetName());
if (f != _scripts.end()){ if (f != _scripts.end()){
@ -28,6 +22,14 @@ namespace CreatureLib::Battling{
void Remove(const std::string& key){ void Remove(const std::string& key){
_scripts.erase(key); _scripts.erase(key);
} }
size_t Count() const{
return _scripts.size();
}
std::unordered_map<std::string, Script *> * GetIterator(){
return &_scripts;
}
}; };
} }

View File

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