#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& Battle::GetLibrary() const noexcept { return _library; } bool Battle::CanUse(const ArbUt::BorrowedPtr& choice) { if (choice->GetKind() == TurnChoiceKind::Attack) { // HOOK: change number of uses needed. return choice.ForceAs()->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>(_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(choice); uint8_t uses = 1; // HOOK: change number of uses needed. if (attack->GetAttack()->GetRemainingUses() < uses) { choice = std::shared_ptr(_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(choices); TriggerEventListener(); 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(); auto end = std::chrono::steady_clock::now(); _lastTurnTime = std::chrono::duration_cast(end - begin).count(); } ArbUt::BorrowedPtr Battle::GetCurrentTurnQueue() const noexcept { return _currentTurnQueue; } BattleRandom* Battle::GetRandom() noexcept { return &_random; } bool Battle::CreatureInField(ArbUt::BorrowedPtr 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& 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(text); } Battle* Battle::Clone() { auto parties = ArbUt::List(_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; }