Initial work on attack fail handling.
continuous-integration/drone/push Build is passing Details

Signed-off-by: Deukhoofd <Deukhoofd@gmail.com>
This commit is contained in:
Deukhoofd 2021-03-27 12:30:12 +01:00
parent 6ef8edc2df
commit 46307fe71f
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
5 changed files with 64 additions and 34 deletions

View File

@ -9,6 +9,7 @@
#include "Events/DisplayTextEvent.hpp" #include "Events/DisplayTextEvent.hpp"
#include "Events/EventData.hpp" #include "Events/EventData.hpp"
#include "Events/ExperienceGainEvent.hpp" #include "Events/ExperienceGainEvent.hpp"
#include "Events/FailEvent.hpp"
#include "Events/FaintEvent.hpp" #include "Events/FaintEvent.hpp"
#include "Events/HealEvent.hpp" #include "Events/HealEvent.hpp"
#include "Events/MissEvent.hpp" #include "Events/MissEvent.hpp"

View File

@ -0,0 +1,16 @@
#ifndef FAILEVENT_HPP
#define FAILEVENT_HPP
#include "EventData.hpp"
namespace CreatureLib::Battling {
class FailEvent final : public EventData {
ArbUt::BorrowedPtr<Creature> _creature;
public:
FailEvent(ArbUt::BorrowedPtr<Creature> c) noexcept : _creature(c) {}
EventDataKind GetKind() const noexcept override { return EventDataKind ::Faint; }
const ArbUt::BorrowedPtr<Creature>& GetCreature() const noexcept { return _creature; }
};
}
#endif // FAILEVENT_HPP

View File

@ -63,6 +63,13 @@ void TurnHandler::ExecuteChoice(ArbUt::BorrowedPtr<BaseTurnChoice> choice) {
} }
} }
#define FAIL_HANDLING(source, user, target) \
battle.GetValue()->TriggerEventListener<FailEvent>(user); \
HOOK(OnFail, source, user) \
if ((target) != (void*)0) { \
HOOK(OnOpponentFail, (target), user) \
}
void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>& choice) { void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>& choice) {
auto battle = choice->GetUser()->GetBattle(); auto battle = choice->GetUser()->GetBattle();
auto attackName = choice->GetAttack()->GetAttack()->GetName(); auto attackName = choice->GetAttack()->GetAttack()->GetName();
@ -77,20 +84,20 @@ void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>
try_creature(targets = TargetResolver::ResolveTargets(choice->GetTarget(), targetType, battle.GetValue()); try_creature(targets = TargetResolver::ResolveTargets(choice->GetTarget(), targetType, battle.GetValue());
, "Exception during target determination"); , "Exception during target determination");
auto attack = new ExecutingAttack(targets, 1, choice->GetUser(), choice->GetAttack(), choice->GetAttackScript()); auto attackScoped = ArbUt::ScopedPtr<ExecutingAttack>(
new ExecutingAttack(targets, 1, choice->GetUser(), choice->GetAttack(), choice->GetAttackScript()));
bool prevented = false; bool prevented = false;
HOOK(PreventAttack, attack, attack, &prevented); HOOK(PreventAttack, attackScoped, attackScoped, &prevented);
if (prevented) { if (prevented) {
delete attack;
return; return;
} }
// HOOK: override targets // HOOK: override targets
if (!choice->GetAttack()->TryUse(1)) { if (!choice->GetAttack()->TryUse(1)) {
delete attack;
return; return;
} }
auto* attack = attackScoped.TakeOwnership();
battle.GetValue()->TriggerEventListener<AttackUseEvent>(attack); battle.GetValue()->TriggerEventListener<AttackUseEvent>(attack);
battle.GetValue()->RegisterHistoryElement<AttackUseHistory>(attack); battle.GetValue()->RegisterHistoryElement<AttackUseHistory>(attack);
@ -98,7 +105,7 @@ void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>
bool fail = false; bool fail = false;
HOOK(FailAttack, attack, attack, &fail); HOOK(FailAttack, attack, attack, &fail);
if (fail) { if (fail) {
// TODO: Fail handling. FAIL_HANDLING(attack, choice->GetUser(), (ScriptSource*)nullptr);
return; return;
} }
@ -120,16 +127,17 @@ void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>
void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::BorrowedPtr<Creature>& target) { void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::BorrowedPtr<Creature>& target) {
EnsureNotNull(attack) EnsureNotNull(attack)
auto& user = attack->GetUser(); const auto& user = attack->GetUser();
auto& battle = user->GetBattle(); const auto& battle = user->GetBattle();
Ensure(battle.HasValue()) Ensure(battle.HasValue())
if (battle.GetValue()->HasEnded()) if (battle.GetValue()->HasEnded()) {
return; return;
}
bool fail = false; bool fail = false;
HOOK(FailIncomingAttack, target, attack, target.GetRaw(), &fail); HOOK(FailIncomingAttack, target, attack, target.GetRaw(), &fail);
if (fail) { if (fail) {
// TODO: Fail handling. FAIL_HANDLING(attack, user, target);
return; return;
} }
@ -147,21 +155,22 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::Bo
return; return;
} }
auto& learnedAttack = attack->GetAttack(); const auto& learnedAttack = attack->GetAttack();
auto& attackData = learnedAttack->GetAttack(); const auto& attackData = learnedAttack->GetAttack();
auto& library = battle.GetValue()->GetLibrary(); const auto& library = battle.GetValue()->GetLibrary();
auto& dmgLibrary = library->GetDamageLibrary(); const auto& dmgLibrary = library->GetDamageLibrary();
auto& typeLibrary = library->GetTypeLibrary(); const auto& typeLibrary = library->GetTypeLibrary();
auto& miscLibrary = library->GetMiscLibrary(); const auto& miscLibrary = library->GetMiscLibrary();
EnsureNotNull(dmgLibrary) EnsureNotNull(dmgLibrary)
EnsureNotNull(typeLibrary) EnsureNotNull(typeLibrary)
EnsureNotNull(miscLibrary) EnsureNotNull(miscLibrary)
auto hitIterator = attack->GetTargetIteratorBegin(target); auto* hitIterator = attack->GetTargetIteratorBegin(target);
for (uint8_t hitIndex = 0; hitIndex < numberOfHits; hitIndex++) { for (uint8_t hitIndex = 0; hitIndex < numberOfHits; hitIndex++) {
if (battle.GetValue()->HasEnded()) if (battle.GetValue()->HasEnded()) {
return; return;
}
if (user->IsFainted()) { if (user->IsFainted()) {
break; break;
} }
@ -182,16 +191,15 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::Bo
if (attackData->GetCategory() == Library::AttackCategory::Status) { if (attackData->GetCategory() == Library::AttackCategory::Status) {
if (attackData->HasSecondaryEffect()) { if (attackData->HasSecondaryEffect()) {
try { try {
auto& effect = attackData->GetSecondaryEffect(); const auto& effect = attackData->GetSecondaryEffect();
bool hasSecondaryEffect; bool hasSecondaryEffect =
if (effect->GetChance() == -1) { effect->GetChance() == -1 ||
hasSecondaryEffect = true;
} else {
hasSecondaryEffect =
battle.GetValue()->GetRandom()->EffectChance(effect->GetChance(), attack, target.GetRaw()); battle.GetValue()->GetRandom()->EffectChance(effect->GetChance(), attack, target.GetRaw());
}
if (hasSecondaryEffect) { if (hasSecondaryEffect) {
HOOK(OnSecondaryEffect, attack, attack, target.GetRaw(), hitIndex); HOOK(OnSecondaryEffect, attack, attack, target.GetRaw(), hitIndex);
if (hit.HasFailed()) {
FAIL_HANDLING(attack, user, target);
}
} }
} catch (const ArbUt::Exception& e) { } catch (const ArbUt::Exception& e) {
throw e; throw e;
@ -213,17 +221,16 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::Bo
HOOK(PreventSecondaryEffects, target, attack, target.GetRaw(), hitIndex, &preventSecondary); HOOK(PreventSecondaryEffects, target, attack, target.GetRaw(), hitIndex, &preventSecondary);
if (!preventSecondary) { if (!preventSecondary) {
try { try {
auto& effect = attackData->GetSecondaryEffect(); const auto& effect = attackData->GetSecondaryEffect();
bool hasSecondaryEffect; bool hasSecondaryEffect =
if (effect->GetChance() == -1) { effect->GetChance() == -1 || battle.GetValue()->GetRandom()->EffectChance(
hasSecondaryEffect = true; effect->GetChance(), attack, target.GetRaw());
} else {
auto random = battle.GetValue()->GetRandom();
EnsureNotNull(random);
hasSecondaryEffect = random->EffectChance(effect->GetChance(), attack, target.GetRaw());
}
if (hasSecondaryEffect) { if (hasSecondaryEffect) {
HOOK(OnSecondaryEffect, attack, attack, target.GetRaw(), hitIndex); HOOK(OnSecondaryEffect, attack, attack, target.GetRaw(), hitIndex);
if (hit.HasFailed()) {
battle.GetValue()->TriggerEventListener<FailEvent>(user);
FAIL_HANDLING(attack, user, target);
}
} }
} catch (const ArbUt::Exception& e) { } catch (const ArbUt::Exception& e) {
throw e; throw e;

View File

@ -12,6 +12,7 @@ namespace CreatureLib::Battling {
float _effectiveness = 1; float _effectiveness = 1;
uint32_t _damage = 0; uint32_t _damage = 0;
uint8_t _type = 0; uint8_t _type = 0;
bool _hasFailed = false;
public: public:
HitData() noexcept {} HitData() noexcept {}
@ -21,12 +22,14 @@ namespace CreatureLib::Battling {
[[nodiscard]] inline float GetEffectiveness() const noexcept { return _effectiveness; } [[nodiscard]] inline float GetEffectiveness() const noexcept { return _effectiveness; }
[[nodiscard]] inline uint32_t GetDamage() const noexcept { return _damage; } [[nodiscard]] inline uint32_t GetDamage() const noexcept { return _damage; }
[[nodiscard]] inline uint8_t GetType() const noexcept { return _type; } [[nodiscard]] inline uint8_t GetType() const noexcept { return _type; }
[[nodiscard]] inline bool HasFailed() const noexcept { return _hasFailed; }
inline void SetCritical(bool value) noexcept { _critical = value; } inline void SetCritical(bool value) noexcept { _critical = value; }
inline void SetBasePower(uint8_t value) noexcept { _basePower = value; } inline void SetBasePower(uint8_t value) noexcept { _basePower = value; }
inline void SetEffectiveness(float value) noexcept { _effectiveness = value; } inline void SetEffectiveness(float value) noexcept { _effectiveness = value; }
inline void SetDamage(uint32_t value) noexcept { _damage = value; } inline void SetDamage(uint32_t value) noexcept { _damage = value; }
inline void SetType(uint8_t value) noexcept { _type = value; } inline void SetType(uint8_t value) noexcept { _type = value; }
inline void Fail() noexcept { _hasFailed = true; }
}; };
private: private:

View File

@ -75,6 +75,9 @@ namespace CreatureLib::Battling {
[[maybe_unused]] Creature* target, [[maybe_unused]] float* chance){}; [[maybe_unused]] Creature* target, [[maybe_unused]] float* chance){};
virtual void ModifyIncomingEffectChance([[maybe_unused]] const ExecutingAttack* attack, virtual void ModifyIncomingEffectChance([[maybe_unused]] const ExecutingAttack* attack,
[[maybe_unused]] Creature* target, [[maybe_unused]] float* chance){}; [[maybe_unused]] Creature* target, [[maybe_unused]] float* chance){};
virtual void OnFail([[maybe_unused]] Creature* target){};
virtual void OnOpponentFail([[maybe_unused]] Creature* target){};
}; };
} }