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/EventData.hpp"
#include "Events/ExperienceGainEvent.hpp"
#include "Events/FailEvent.hpp"
#include "Events/FaintEvent.hpp"
#include "Events/HealEvent.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) {
auto battle = choice->GetUser()->GetBattle();
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());
, "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;
HOOK(PreventAttack, attack, attack, &prevented);
HOOK(PreventAttack, attackScoped, attackScoped, &prevented);
if (prevented) {
delete attack;
return;
}
// HOOK: override targets
if (!choice->GetAttack()->TryUse(1)) {
delete attack;
return;
}
auto* attack = attackScoped.TakeOwnership();
battle.GetValue()->TriggerEventListener<AttackUseEvent>(attack);
battle.GetValue()->RegisterHistoryElement<AttackUseHistory>(attack);
@ -98,7 +105,7 @@ void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>
bool fail = false;
HOOK(FailAttack, attack, attack, &fail);
if (fail) {
// TODO: Fail handling.
FAIL_HANDLING(attack, choice->GetUser(), (ScriptSource*)nullptr);
return;
}
@ -120,16 +127,17 @@ void TurnHandler::ExecuteAttackChoice(const ArbUt::BorrowedPtr<AttackTurnChoice>
void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::BorrowedPtr<Creature>& target) {
EnsureNotNull(attack)
auto& user = attack->GetUser();
auto& battle = user->GetBattle();
const auto& user = attack->GetUser();
const auto& battle = user->GetBattle();
Ensure(battle.HasValue())
if (battle.GetValue()->HasEnded())
if (battle.GetValue()->HasEnded()) {
return;
}
bool fail = false;
HOOK(FailIncomingAttack, target, attack, target.GetRaw(), &fail);
if (fail) {
// TODO: Fail handling.
FAIL_HANDLING(attack, user, target);
return;
}
@ -147,21 +155,22 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::Bo
return;
}
auto& learnedAttack = attack->GetAttack();
auto& attackData = learnedAttack->GetAttack();
const auto& learnedAttack = attack->GetAttack();
const auto& attackData = learnedAttack->GetAttack();
auto& library = battle.GetValue()->GetLibrary();
auto& dmgLibrary = library->GetDamageLibrary();
auto& typeLibrary = library->GetTypeLibrary();
auto& miscLibrary = library->GetMiscLibrary();
const auto& library = battle.GetValue()->GetLibrary();
const auto& dmgLibrary = library->GetDamageLibrary();
const auto& typeLibrary = library->GetTypeLibrary();
const auto& miscLibrary = library->GetMiscLibrary();
EnsureNotNull(dmgLibrary)
EnsureNotNull(typeLibrary)
EnsureNotNull(miscLibrary)
auto hitIterator = attack->GetTargetIteratorBegin(target);
auto* hitIterator = attack->GetTargetIteratorBegin(target);
for (uint8_t hitIndex = 0; hitIndex < numberOfHits; hitIndex++) {
if (battle.GetValue()->HasEnded())
if (battle.GetValue()->HasEnded()) {
return;
}
if (user->IsFainted()) {
break;
}
@ -182,16 +191,15 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::Bo
if (attackData->GetCategory() == Library::AttackCategory::Status) {
if (attackData->HasSecondaryEffect()) {
try {
auto& effect = attackData->GetSecondaryEffect();
bool hasSecondaryEffect;
if (effect->GetChance() == -1) {
hasSecondaryEffect = true;
} else {
hasSecondaryEffect =
battle.GetValue()->GetRandom()->EffectChance(effect->GetChance(), attack, target.GetRaw());
}
const auto& effect = attackData->GetSecondaryEffect();
bool hasSecondaryEffect =
effect->GetChance() == -1 ||
battle.GetValue()->GetRandom()->EffectChance(effect->GetChance(), attack, target.GetRaw());
if (hasSecondaryEffect) {
HOOK(OnSecondaryEffect, attack, attack, target.GetRaw(), hitIndex);
if (hit.HasFailed()) {
FAIL_HANDLING(attack, user, target);
}
}
} catch (const ArbUt::Exception& e) {
throw e;
@ -213,17 +221,16 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, const ArbUt::Bo
HOOK(PreventSecondaryEffects, target, attack, target.GetRaw(), hitIndex, &preventSecondary);
if (!preventSecondary) {
try {
auto& effect = attackData->GetSecondaryEffect();
bool hasSecondaryEffect;
if (effect->GetChance() == -1) {
hasSecondaryEffect = true;
} else {
auto random = battle.GetValue()->GetRandom();
EnsureNotNull(random);
hasSecondaryEffect = random->EffectChance(effect->GetChance(), attack, target.GetRaw());
}
const auto& effect = attackData->GetSecondaryEffect();
bool hasSecondaryEffect =
effect->GetChance() == -1 || battle.GetValue()->GetRandom()->EffectChance(
effect->GetChance(), attack, target.GetRaw());
if (hasSecondaryEffect) {
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) {
throw e;

View File

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

View File

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