CreatureLib/src/Battling/Models/Battle.cpp

213 lines
7.6 KiB
C++

#include "Battle.hpp"
#include "../EventHooks/EventDataClasses.hpp"
#include "../Flow/TurnHandler.hpp"
#include "../Flow/TurnOrdering.hpp"
using namespace CreatureLib;
using namespace CreatureLib::Battling;
const ArbUt::BorrowedPtr<const BattleLibrary>& Battle::GetLibrary() const noexcept { return _library; }
bool Battle::CanUse(const ArbUt::BorrowedPtr<BaseTurnChoice>& choice) {
if (choice->GetKind() == TurnChoiceKind::Attack) {
// HOOK: change number of uses needed.
return choice.ForceAs<AttackTurnChoice>()->GetAttack()->GetRemainingUses() >= 1;
}
return true;
}
bool Battle::TrySetChoice(BaseTurnChoice* choice) {
EnsureNotNull(choice)
if (!CanUse(choice)) {
return false;
}
Ensure(choice->GetUser()->GetBattleSide().HasValue())
choice->GetUser()->GetBattleSide().GetValue()->SetChoice(choice);
CheckChoicesSetAndRun();
return true;
}
void Battle::CheckChoicesSetAndRun() {
try {
for (auto* side : _sides) {
if (!side->AllChoicesSet()) {
return;
}
}
for (auto* side : _sides) {
if (!side->AllPossibleSlotsFilled()) {
return;
}
}
} catch (const ArbUt::Exception& e) {
throw e;
} catch (const std::exception& e) {
THROW("Exception during choices set validation: '" << e.what() << "'.")
}
auto begin = std::chrono::steady_clock::now();
auto choices = std::vector<std::shared_ptr<BaseTurnChoice>>(_numberOfSides * _creaturesPerSide);
auto i = 0;
try {
for (auto* side : _sides) {
for (auto choice : side->GetChoices()) {
EnsureNotNull(choice)
if (choice->GetKind() == TurnChoiceKind::Attack) {
auto attack = std::static_pointer_cast<AttackTurnChoice>(choice);
uint8_t uses = 1;
// HOOK: change number of uses needed.
if (attack->GetAttack()->GetRemainingUses() < uses) {
choice = std::shared_ptr<BaseTurnChoice>(_library->GetMiscLibrary()->ReplacementAttack(
choice->GetUser().GetRaw(), attack->GetTarget()));
}
// HOOK: Check if we need to change the move
}
choice->__SetRandomValue(_random.Get());
choices[i] = choice;
i++;
}
side->ResetChoices();
}
} catch (const std::exception& e) {
THROW("Exception during turn initialization: '" << e.what() << "'.")
}
_currentTurn++;
try {
TurnOrdering::OrderChoices(choices);
} catch (const std::exception& e) {
THROW("Exception during turn ordering: '" << e.what() << "'.")
}
this->_currentTurnQueue = std::make_unique<ChoiceQueue>(choices);
TriggerEventListener<TurnStartEvent>();
try {
TurnHandler::RunTurn(this->_currentTurnQueue, this);
} catch (const ArbUt::Exception& e) {
throw e;
} catch (const std::exception& e) {
THROW("Error during running a turn: '" << e.what() << "'.");
}
if (this->_currentTurnQueue->HasCompletedQueue) {
this->_currentTurnQueue = nullptr;
}
TriggerEventListener<TurnEndEvent>();
auto end = std::chrono::steady_clock::now();
_lastTurnTime = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
}
ArbUt::BorrowedPtr<ChoiceQueue> Battle::GetCurrentTurnQueue() const noexcept { return _currentTurnQueue; }
BattleRandom* Battle::GetRandom() noexcept { return &_random; }
bool Battle::CreatureInField(ArbUt::BorrowedPtr<Creature> creature) const {
return std::any_of(_sides.begin(), _sides.end(), [creature](auto c) { return c->CreatureOnSide(creature); });
}
void Battle::ForceRecall(uint8_t side, uint8_t index) { _sides[side]->SetCreature(nullptr, index); }
size_t Battle::ScriptCount() const { return 1; }
void Battle::GetActiveScripts(ArbUt::List<ScriptWrapper>& scripts) {
scripts.Append(ScriptWrapper::FromSet(&_volatile));
}
void Battle::SwitchCreature(uint8_t sideIndex, uint8_t index, Creature* c) {
auto side = this->_sides[sideIndex];
side->SetCreature(c, index);
}
bool Battle::CanSlotBeFilled(uint8_t side, uint8_t index) const {
for (const auto& party : _parties) {
if (party->IsResponsibleForIndex(side, index)) {
if (party->HasCreaturesNotInField())
return true;
}
}
return false;
}
void Battle::ValidateBattleState() {
if (_hasEnded) {
return;
}
bool survivingSideExists = false;
uint8_t winningSide = 255;
for (uint8_t i = 0; i < _sides.Count(); i++) {
auto side = _sides[i];
if (side->HasFled()) {
this->_battleResult = BattleResult::Inconclusive();
EndBattle();
return;
}
if (!side->IsDefeated()) {
if (survivingSideExists) {
return;
}
survivingSideExists = true;
winningSide = i;
}
}
if (!survivingSideExists) {
this->_battleResult = BattleResult::Inconclusive();
EndBattle();
return;
}
this->_battleResult = BattleResult::Conclusive(winningSide);
EndBattle();
}
BattleScript* Battle::AddVolatileScript(const ArbUt::StringView& key) {
auto script = _volatile.Get(key);
if (script.HasValue()) {
script.GetValue()->Stack();
return script.GetValue();
}
script = _library->LoadScript(ScriptCategory::Battle, key);
if (!script.HasValue()) {
THROW("Invalid volatile script requested for battle: '" << key.c_str() << "'.");
}
return _volatile.Add(script.GetValue());
}
BattleScript* Battle::AddVolatileScript(BattleScript* script) { return _volatile.Add(script); }
void Battle::RemoveVolatileScript(BattleScript* script) { _volatile.Remove(script->GetName()); }
void Battle::DisplayText(const ArbUt::StringView& text) { TriggerEventListener<DisplayTextEvent>(text); }
Battle* Battle::Clone() {
auto parties = ArbUt::List<BattleParty*>(_parties.Count());
for (auto* party : _parties) {
parties.Append(party->Clone());
}
auto* battle = new Battle(_library, parties, _canFlee, _numberOfSides, _creaturesPerSide);
for (int i = 0; i < _numberOfSides; ++i) {
battle->_sides.Set(i, _sides[i]->CloneWithoutCreatures());
// FIXME: This is horrible code to translate the creature from the old battle into the same creature in the
// new battle. This needs to be cleaned up, as it's ugly and slow.
for (int creatureIndex = 0; creatureIndex < _creaturesPerSide; ++creatureIndex) {
auto creature = _sides[i]->GetCreature(creatureIndex);
if (!creature.HasValue()) {
continue;
}
for (size_t j = 0; j < _parties.Count(); ++j) {
auto party = _parties[j];
if (!party->IsResponsibleForIndex(i, creatureIndex)) {
continue;
}
auto partyIndex = party->GetParty()->GetParty().IndexOf(creature.GetValue());
if (partyIndex != -1U) {
auto c = battle->_parties.At(j)->GetParty()->GetParty()[partyIndex];
battle->_sides.At(i)->SetCreature(c, creatureIndex);
j = _parties.Count();
break;
}
}
}
}
battle->_random = _random;
battle->_hasEnded = _hasEnded;
battle->_battleResult = _battleResult;
_historyHolder.CloneOnto(battle->_historyHolder);
battle->_currentTurn = _currentTurn;
_volatile.Clone(battle->_volatile);
return battle;
}