Implements running from battle.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
0fad615050
commit
6ba708ad12
|
@ -44,9 +44,9 @@ void TurnHandler::ExecuteChoice(BaseTurnChoice* choice) {
|
|||
case TurnChoiceKind::Pass: throw NotReachableException();
|
||||
case TurnChoiceKind::Attack: return ExecuteAttackChoice(dynamic_cast<AttackTurnChoice*>(choice));
|
||||
case TurnChoiceKind::Switch: return ExecuteSwitchChoice(dynamic_cast<SwitchTurnChoice*>(choice));
|
||||
case TurnChoiceKind::Flee: return ExecuteFleeChoice(dynamic_cast<FleeTurnChoice*>(choice));
|
||||
|
||||
case TurnChoiceKind::Item:
|
||||
case TurnChoiceKind::RunAway: throw NotImplementedException();
|
||||
case TurnChoiceKind::Item: throw NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ void TurnHandler::HandleAttackForTarget(ExecutingAttack* attack, Creature* targe
|
|||
auto hitType = hit->GetType();
|
||||
HOOK(ChangeAttackType, targetSource, attack, target, hitIndex, hitType);
|
||||
hit->SetEffectiveness(library->GetTypeLibrary()->GetEffectiveness(hitType, target->GetTypes()));
|
||||
hit->SetCritical(library->GetCriticalLibrary()->IsCritical(attack, target, hitIndex));
|
||||
hit->SetCritical(library->GetMiscLibrary()->IsCritical(attack, target, hitIndex));
|
||||
hit->SetBasePower(dmgLibrary->GetBasePower(attack, target, hitIndex));
|
||||
hit->SetDamage(dmgLibrary->GetDamage(attack, target, hitIndex));
|
||||
|
||||
|
@ -181,3 +181,17 @@ void TurnHandler::ExecuteSwitchChoice(CreatureLib::Battling::SwitchTurnChoice* c
|
|||
auto userIndex = userSide->GetCreatureIndex(user);
|
||||
userSide->SetCreature(choice->GetNewCreature(), userIndex);
|
||||
}
|
||||
void TurnHandler::ExecuteFleeChoice(FleeTurnChoice* choice) {
|
||||
auto user = choice->GetUser();
|
||||
auto battle = user->GetBattle();
|
||||
if (!battle->CanFlee()) {
|
||||
return;
|
||||
}
|
||||
// TODO: If any of the creatures on the users side has a script that prevents it from running, block.
|
||||
// TODO: If any of the creatures on any other side has a script that prevents this side from running, block.
|
||||
|
||||
if (battle->GetLibrary()->GetMiscLibrary()->CanFlee(choice)) {
|
||||
user->GetBattleSide()->MarkAsFled();
|
||||
battle->ValidateBattleState();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "../Models/ExecutingAttack.hpp"
|
||||
#include "../TurnChoices/AttackTurnChoice.hpp"
|
||||
#include "../TurnChoices/FleeTurnChoice.hpp"
|
||||
#include "../TurnChoices/SwitchTurnChoice.hpp"
|
||||
#include "ChoiceQueue.hpp"
|
||||
|
||||
|
@ -17,6 +18,7 @@ namespace CreatureLib::Battling {
|
|||
ExecutingAttack::TargetData* targetData);
|
||||
|
||||
static void ExecuteSwitchChoice(SwitchTurnChoice* choice);
|
||||
static void ExecuteFleeChoice(FleeTurnChoice* choice);
|
||||
|
||||
public:
|
||||
static void RunTurn(Battle* battle, ChoiceQueue* queue);
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
using namespace CreatureLib::Battling;
|
||||
|
||||
BattleLibrary::BattleLibrary(CreatureLib::Library::DataLibrary* staticLib, BattleStatCalculator* statCalculator,
|
||||
DamageLibrary* damageLibrary, CriticalLibrary* criticalLibrary,
|
||||
ExperienceLibrary* experienceLibrary, ScriptResolver* scriptResolver)
|
||||
DamageLibrary* damageLibrary, ExperienceLibrary* experienceLibrary,
|
||||
ScriptResolver* scriptResolver, MiscLibrary* miscLibrary)
|
||||
: _staticLib(staticLib), _statCalculator(statCalculator), _damageLibrary(damageLibrary),
|
||||
_criticalLibrary(criticalLibrary), _experienceLibrary(experienceLibrary), _scriptResolver(scriptResolver) {}
|
||||
_experienceLibrary(experienceLibrary), _scriptResolver(scriptResolver), _miscLibrary(miscLibrary) {}
|
||||
|
||||
BattleLibrary::~BattleLibrary() {
|
||||
delete _staticLib;
|
||||
delete _statCalculator;
|
||||
delete _damageLibrary;
|
||||
delete _criticalLibrary;
|
||||
delete _experienceLibrary;
|
||||
delete _scriptResolver;
|
||||
delete _miscLibrary;
|
||||
}
|
||||
|
||||
const CreatureLib::Library::LibrarySettings& BattleLibrary::GetSettings() const { return _staticLib->GetSettings(); }
|
||||
|
@ -36,7 +36,7 @@ const CreatureLib::Library::TypeLibrary* BattleLibrary::GetTypeLibrary() const {
|
|||
|
||||
const DamageLibrary* BattleLibrary::GetDamageLibrary() const { return _damageLibrary; }
|
||||
|
||||
const CriticalLibrary* BattleLibrary::GetCriticalLibrary() const { return _criticalLibrary; }
|
||||
const MiscLibrary* BattleLibrary::GetMiscLibrary() const { return _miscLibrary; }
|
||||
|
||||
Script* BattleLibrary::LoadScript(ScriptResolver::ScriptCategory category, const std::string& scriptName) const {
|
||||
return _scriptResolver->LoadScript(category, scriptName);
|
||||
|
|
|
@ -4,23 +4,23 @@
|
|||
#include "../../Library/DataLibrary.hpp"
|
||||
#include "../ScriptHandling/ScriptResolver.hpp"
|
||||
#include "BattleStatCalculator.hpp"
|
||||
#include "CriticalLibrary.hpp"
|
||||
#include "DamageLibrary.hpp"
|
||||
#include "ExperienceLibrary.hpp"
|
||||
#include "MiscLibrary.hpp"
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
class BattleLibrary {
|
||||
const Library::DataLibrary* _staticLib = nullptr;
|
||||
BattleStatCalculator* _statCalculator = nullptr;
|
||||
DamageLibrary* _damageLibrary = nullptr;
|
||||
CriticalLibrary* _criticalLibrary = nullptr;
|
||||
ExperienceLibrary* _experienceLibrary = nullptr;
|
||||
ScriptResolver* _scriptResolver = nullptr;
|
||||
MiscLibrary* _miscLibrary = nullptr;
|
||||
|
||||
public:
|
||||
BattleLibrary(Library::DataLibrary* staticLib, BattleStatCalculator* statCalculator,
|
||||
DamageLibrary* damageLibrary, CriticalLibrary* criticalLibrary,
|
||||
ExperienceLibrary* experienceLibrary, ScriptResolver* scriptResolver);
|
||||
DamageLibrary* damageLibrary, ExperienceLibrary* experienceLibrary,
|
||||
ScriptResolver* scriptResolver, MiscLibrary* miscLibrary);
|
||||
~BattleLibrary();
|
||||
|
||||
[[nodiscard]] const Library::LibrarySettings& GetSettings() const;
|
||||
|
@ -34,7 +34,7 @@ namespace CreatureLib::Battling {
|
|||
|
||||
[[nodiscard]] const BattleStatCalculator* GetStatCalculator() const;
|
||||
[[nodiscard]] const DamageLibrary* GetDamageLibrary() const;
|
||||
[[nodiscard]] const CriticalLibrary* GetCriticalLibrary() const;
|
||||
[[nodiscard]] const MiscLibrary* GetMiscLibrary() const;
|
||||
[[nodiscard]] const ExperienceLibrary* GetExperienceLibrary() const { return _experienceLibrary; }
|
||||
|
||||
[[nodiscard]] Script* LoadScript(ScriptResolver::ScriptCategory category, const std::string& scriptName) const;
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#include "CriticalLibrary.hpp"
|
||||
#include "../Models/Battle.hpp"
|
||||
|
||||
bool CreatureLib::Battling::CriticalLibrary::IsCritical(CreatureLib::Battling::ExecutingAttack* attack,
|
||||
CreatureLib::Battling::Creature* target, uint8_t hit) const {
|
||||
auto rand = target->GetBattle()->GetRandom();
|
||||
// HOOK: Increase chance for critical hits.
|
||||
return rand.Get(10) <= 0;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef CREATURELIB_CRITICALLIBRARY_HPP
|
||||
#define CREATURELIB_CRITICALLIBRARY_HPP
|
||||
|
||||
#include "../Models/ExecutingAttack.hpp"
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
class CriticalLibrary {
|
||||
public:
|
||||
virtual ~CriticalLibrary() = default;
|
||||
virtual bool IsCritical(ExecutingAttack* attack, Creature* target, uint8_t hit) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_CRITICALLIBRARY_HPP
|
|
@ -0,0 +1,10 @@
|
|||
#include "MiscLibrary.hpp"
|
||||
#include "../Models/Battle.hpp"
|
||||
|
||||
bool CreatureLib::Battling::MiscLibrary::IsCritical(CreatureLib::Battling::ExecutingAttack* attack,
|
||||
CreatureLib::Battling::Creature* target, uint8_t hit) const {
|
||||
auto rand = target->GetBattle()->GetRandom();
|
||||
// HOOK: Increase chance for critical hits.
|
||||
return rand.Get(10) <= 0;
|
||||
}
|
||||
bool CreatureLib::Battling::MiscLibrary::CanFlee(FleeTurnChoice* switchChoice) const { return true; }
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef CREATURELIB_MISCLIBRARY_HPP
|
||||
#define CREATURELIB_MISCLIBRARY_HPP
|
||||
|
||||
#include "../Models/ExecutingAttack.hpp"
|
||||
#include "../TurnChoices/FleeTurnChoice.hpp"
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
class MiscLibrary {
|
||||
public:
|
||||
virtual ~MiscLibrary() = default;
|
||||
virtual bool IsCritical(ExecutingAttack* attack, Creature* target, uint8_t hit) const;
|
||||
virtual bool CanFlee(FleeTurnChoice* switchChoice) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_MISCLIBRARY_HPP
|
|
@ -100,17 +100,22 @@ bool Battle::CanSlotBeFilled(uint8_t side, uint8_t index) const {
|
|||
|
||||
void Battle::ValidateBattleState() {
|
||||
bool survivingSideExists = false;
|
||||
uint8_t result = 0;
|
||||
uint8_t winningSide = 0;
|
||||
for (uint8_t i = 0; i < _sides.size(); i++) {
|
||||
auto side = _sides[i];
|
||||
if (side->HasFled()) {
|
||||
this->_battleResult = BattleResult::Inconclusive();
|
||||
this->_hasEnded = true;
|
||||
return;
|
||||
}
|
||||
if (!side->IsDefeated()) {
|
||||
if (survivingSideExists) {
|
||||
return;
|
||||
}
|
||||
survivingSideExists = true;
|
||||
result = i;
|
||||
winningSide = i;
|
||||
}
|
||||
}
|
||||
this->_battleResult = result;
|
||||
this->_battleResult = BattleResult::Conclusive(winningSide);
|
||||
this->_hasEnded = true;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "../Library/BattleLibrary.hpp"
|
||||
#include "../TurnChoices/BaseTurnChoice.hpp"
|
||||
#include "BattleParty.hpp"
|
||||
#include "BattleResult.hpp"
|
||||
#include "BattleSide.hpp"
|
||||
#include "CreatureIndex.hpp"
|
||||
|
||||
|
@ -13,20 +14,22 @@ namespace CreatureLib::Battling {
|
|||
class Battle : public ScriptSource {
|
||||
const BattleLibrary* _library;
|
||||
std::vector<BattleParty> _parties;
|
||||
bool _canFlee;
|
||||
uint8_t _numberOfSides;
|
||||
uint8_t _creaturesPerSide;
|
||||
std::vector<BattleSide*> _sides;
|
||||
Core::Random _random;
|
||||
ChoiceQueue* _currentTurnQueue = nullptr;
|
||||
bool _hasEnded = false;
|
||||
uint8_t _battleResult = 0;
|
||||
BattleResult _battleResult = BattleResult::Empty();
|
||||
|
||||
ScriptSet _volatile;
|
||||
|
||||
public:
|
||||
Battle(const BattleLibrary* library, std::vector<BattleParty> parties, uint8_t numberOfSides = 2,
|
||||
uint8_t creaturesPerSide = 1)
|
||||
: _library(library), _parties(parties), _numberOfSides(numberOfSides), _creaturesPerSide(creaturesPerSide) {
|
||||
Battle(const BattleLibrary* library, std::vector<BattleParty> parties, bool canFlee = true,
|
||||
uint8_t numberOfSides = 2, uint8_t creaturesPerSide = 1)
|
||||
: _library(library), _parties(parties), _canFlee(canFlee), _numberOfSides(numberOfSides),
|
||||
_creaturesPerSide(creaturesPerSide) {
|
||||
_sides = std::vector<BattleSide*>(numberOfSides);
|
||||
for (size_t i = 0; i < numberOfSides; i++) {
|
||||
_sides[i] = new BattleSide(i, this, creaturesPerSide);
|
||||
|
@ -44,6 +47,8 @@ namespace CreatureLib::Battling {
|
|||
virtual bool CanUse(const BaseTurnChoice* choice);
|
||||
virtual bool TrySetChoice(BaseTurnChoice* choice);
|
||||
|
||||
bool CanFlee() const { return _canFlee; }
|
||||
|
||||
void CheckChoicesSetAndRun();
|
||||
|
||||
[[nodiscard]] ChoiceQueue* GetCurrentTurnQueue() const;
|
||||
|
@ -63,7 +68,7 @@ namespace CreatureLib::Battling {
|
|||
|
||||
void ValidateBattleState();
|
||||
inline bool HasEnded() const { return _hasEnded; }
|
||||
inline uint8_t GetResult() const { return _battleResult; }
|
||||
inline const BattleResult& GetResult() const { return _battleResult; }
|
||||
|
||||
const std::vector<BattleSide*>& GetSides() const { return _sides; }
|
||||
};
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef CREATURELIB_BATTLERESULT_HPP
|
||||
#define CREATURELIB_BATTLERESULT_HPP
|
||||
|
||||
#include <cstdint>
|
||||
namespace CreatureLib::Battling {
|
||||
class BattleResult {
|
||||
bool _conclusiveResult;
|
||||
uint8_t _winningSide;
|
||||
|
||||
BattleResult(bool conclusiveResult, uint8_t winningSide)
|
||||
: _conclusiveResult(conclusiveResult), _winningSide(winningSide) {}
|
||||
|
||||
public:
|
||||
static BattleResult Inconclusive() { return BattleResult(false, 0); }
|
||||
static BattleResult Conclusive(uint8_t winner) { return BattleResult(true, winner); }
|
||||
static BattleResult Empty() { return BattleResult(false, 0); }
|
||||
|
||||
/// Whether or not the battle has ended with a conclusive result.
|
||||
bool IsConclusiveResult() const { return _conclusiveResult; }
|
||||
/// Get the index of the side that has won the battle. Only valid if the battle has a conclusive result.
|
||||
uint8_t GetWinningSide() const { return _winningSide; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_BATTLERESULT_HPP
|
|
@ -15,6 +15,7 @@ namespace CreatureLib::Battling {
|
|||
uint8_t _choicesSet = 0;
|
||||
ScriptSet _volatile;
|
||||
Battle* _battle;
|
||||
bool _hasFled = false;
|
||||
|
||||
public:
|
||||
explicit BattleSide(uint8_t index, Battle* battle, uint8_t creaturesPerSide)
|
||||
|
@ -74,6 +75,10 @@ namespace CreatureLib::Battling {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HasFled() { return _hasFled; }
|
||||
|
||||
void MarkAsFled() { _hasFled = true; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef CREATURELIB_FLEETURNCHOICE_HPP
|
||||
#define CREATURELIB_FLEETURNCHOICE_HPP
|
||||
|
||||
#include "../Models/Creature.hpp"
|
||||
#include "BaseTurnChoice.hpp"
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
class FleeTurnChoice : public BaseTurnChoice {
|
||||
public:
|
||||
FleeTurnChoice(Creature* user) : BaseTurnChoice(user) {}
|
||||
|
||||
TurnChoiceKind GetKind() const override { return TurnChoiceKind ::Flee; }
|
||||
|
||||
protected:
|
||||
void GetActiveScripts(std::vector<ScriptWrapper>& scripts) override { GetUser()->GetActiveScripts(scripts); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_FLEETURNCHOICE_HPP
|
|
@ -4,6 +4,6 @@
|
|||
#include <cstdint>
|
||||
|
||||
namespace CreatureLib::Battling {
|
||||
enum class TurnChoiceKind : uint8_t { Pass, Attack, Item, Switch, RunAway };
|
||||
enum class TurnChoiceKind : uint8_t { Pass, Attack, Item, Switch, Flee };
|
||||
}
|
||||
#endif // CREATURELIB_TURNCHOICEKIND_HPP
|
||||
|
|
|
@ -79,7 +79,9 @@ TEST_CASE("Finish battle when all battle of one side have fainted", "[Integratio
|
|||
c2->Damage(c2->GetCurrentHealth(), DamageSource::AttackDamage);
|
||||
|
||||
REQUIRE(battle.HasEnded());
|
||||
REQUIRE(battle.GetResult() == 0);
|
||||
auto result = battle.GetResult();
|
||||
REQUIRE(result.IsConclusiveResult());
|
||||
REQUIRE(result.GetWinningSide() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("When creature is dealt enough damage, faint it and mark battle as ended", "[Integrations]") {
|
||||
|
@ -104,7 +106,9 @@ TEST_CASE("When creature is dealt enough damage, faint it and mark battle as end
|
|||
battle.TrySetChoice(new PassTurnChoice(c2));
|
||||
|
||||
REQUIRE(battle.HasEnded());
|
||||
REQUIRE(battle.GetResult() == 0);
|
||||
auto result = battle.GetResult();
|
||||
REQUIRE(result.IsConclusiveResult());
|
||||
REQUIRE(result.GetWinningSide() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("When another creature is available on faint, make sure the battle hasn't ended", "[Integrations]") {
|
||||
|
@ -137,7 +141,9 @@ TEST_CASE("When another creature is available on faint, make sure the battle has
|
|||
battle.TrySetChoice(new PassTurnChoice(c3));
|
||||
|
||||
REQUIRE(battle.HasEnded());
|
||||
REQUIRE(battle.GetResult() == 0);
|
||||
auto result = battle.GetResult();
|
||||
REQUIRE(result.IsConclusiveResult());
|
||||
REQUIRE(result.GetWinningSide() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Switch Creature in", "[Integrations]") {
|
||||
|
@ -221,4 +227,26 @@ TEST_CASE("Switch Creature in, mark as seen opponent for opponent", "[Integratio
|
|||
REQUIRE(seen.find(c2) != seen.end());
|
||||
}
|
||||
|
||||
TEST_CASE("Flee Battle", "[Integrations]") {
|
||||
auto library = TestLibrary::Get();
|
||||
auto c1 = CreateCreature(library, "testSpecies1", 100).WithAttack("standard", AttackLearnMethod::Unknown)->Create();
|
||||
CreatureParty party1{c1};
|
||||
auto battleParty1 = BattleParty(&party1, {CreatureIndex(0, 0)});
|
||||
auto c2 = CreateCreature(library, "testSpecies1", 1).WithAttack("standard", AttackLearnMethod::Unknown)->Create();
|
||||
CreatureParty party2{c2};
|
||||
auto battleParty2 = BattleParty(&party2, {CreatureIndex(1, 0)});
|
||||
|
||||
auto battle = Battle(library, {battleParty1, battleParty2});
|
||||
|
||||
battle.SwitchCreature(0, 0, c1);
|
||||
battle.SwitchCreature(1, 0, c2);
|
||||
|
||||
battle.TrySetChoice(new FleeTurnChoice(c1));
|
||||
battle.TrySetChoice(new PassTurnChoice(c2));
|
||||
|
||||
REQUIRE(battle.HasEnded());
|
||||
auto result = battle.GetResult();
|
||||
REQUIRE_FALSE(result.IsConclusiveResult());
|
||||
}
|
||||
|
||||
#endif
|
|
@ -13,8 +13,8 @@ BattleLibrary* TestLibrary::Get() {
|
|||
auto l = new DataLibrary(LibrarySettings(100, 4), BuildSpeciesLibrary(), BuildAttackLibrary(),
|
||||
BuildItemLibrary(), BuildGrowthRateLibrary(), BuildTypeLibrary());
|
||||
auto statCalc = new BattleStatCalculator();
|
||||
auto battleLib = new BattleLibrary(l, statCalc, new DamageLibrary(), new CriticalLibrary(),
|
||||
new ExperienceLibrary(), new ScriptResolver());
|
||||
auto battleLib = new BattleLibrary(l, statCalc, new DamageLibrary(), new ExperienceLibrary(),
|
||||
new ScriptResolver(), new MiscLibrary());
|
||||
TestLibrary::_library = battleLib;
|
||||
}
|
||||
return TestLibrary::_library;
|
||||
|
|
Loading…
Reference in New Issue