#include "TurnHandler.hpp" #include "../Models/Battle.hpp" #include "../../Core/Exceptions/NotImplementedException.hpp" #include "../ScriptHandling/ScriptMacros.cpp" using namespace CreatureLib::Battling; void TurnHandler::RunTurn(Battle* battle, ChoiceQueue* queue) { //HOOK: On Before Turn hook for all choices while (queue->HasNext()){ if (!battle->HasRecalledSlots()){ return; } auto item = queue->Dequeue(); ExecuteChoice(item); delete item; } queue->HasCompletedQueue = true; } void TurnHandler::ExecuteChoice(const BaseTurnChoice *choice) { if (choice == nullptr) { return; } auto choiceKind = choice->GetKind(); if (choiceKind == TurnChoiceKind::Pass) { return; } auto user = choice->GetUser(); // If the user is fainted, we don't want to execute its choice. if (user->IsFainted()){ return; } auto battle = user->GetBattle(); // If the user is not in the field, we don't want to execute its choice. if (!battle->CreatureInField(user)){ return; } // If the choice is not valid, we don't want to execute it. if (!battle->CanUse(choice)){ return; } switch (choiceKind){ case TurnChoiceKind::Pass: throw NotReachableException(); case TurnChoiceKind::Attack: return ExecuteAttackChoice(dynamic_cast(choice)); case TurnChoiceKind::Item: case TurnChoiceKind::Switch: case TurnChoiceKind::RunAway: throw NotImplementedException(); } } void TurnHandler::ExecuteAttackChoice(const AttackTurnChoice *choice) { //HOOK: Change attack //HOOK: Prevent attack auto attack = ExecutingAttack(); //HOOK: override targets if (!choice->GetAttack()->TryUse(1)){ return; } //HOOK: check if attack fails //HOOK: Check if attack stops after decreasing PP //HOOK: On Before Attack for (auto kv: attack.GetTargets()){ HandleAttackForTarget(attack, kv.first, kv.second); } } void TurnHandler::HandleAttackForTarget(ExecutingAttack &attack, Creature *target, ExecutingAttack::TargetData &targetData) { auto user = attack.GetUser(); std::array targetSources = {target}; std::array userSources = {user}; bool fail = false; HOOK(FailIncomingAttack, targetSources, &attack, target, fail); if (fail){ //TODO: Fail handling. return; } bool invulnerable = fail; HOOK(IsInvulnerable, targetSources, &attack, target, invulnerable); if (invulnerable){ //TODO: We should probably do something when a target is invulnerable. return; } if (!targetData.IsHit()){ HOOK(OnAttackMiss, targetSources, &attack, target); return; } auto numHits = targetData.GetNumberOfHits(); if (numHits == 0) return; auto attackData = attack.GetAttack()->GetAttack(); auto library = user->GetBattle()->GetLibrary(); auto dmgLibrary = library->GetDamageLibrary(); for (uint8_t hitIndex = 0; hitIndex < numHits; hitIndex++){ if (user->IsFainted()){ break; } if (target->IsFainted()){ // STOP, STOP! HE'S ALREADY DEAD ;_; break; } auto hit = targetData.GetHit(hitIndex); auto hitType = hit.GetType(); HOOK(ChangeAttackType, targetSources, &attack, target, hitIndex, hitType); hit.SetEffectiveness(library->GetTypeLibrary()->GetEffectiveness(hitType, target->GetTypes())); hit.SetCritical(library->GetCriticalLibrary()->IsCritical(&attack, target, hitIndex)); hit.SetBasePower(dmgLibrary->GetBasePower(&attack, target, hitIndex)); hit.SetDamage(dmgLibrary->GetDamage(&attack, target, hitIndex)); std::array attackSource = {&attack}; if (attackData->GetCategory() == Library::AttackCategory::Status){ HOOK(OnStatusMove, attackSource, &attack, target, hitIndex); } else{ auto damage = hit.GetDamage(); if (damage > target->GetCurrentHealth()){ damage = target->GetCurrentHealth(); hit.SetDamage(damage); } if (damage > 0){ target->Damage(damage, DamageSource::AttackDamage); bool preventSecondary = false; HOOK(PreventSecondaryEffects, targetSources, &attack, target, hitIndex, preventSecondary); if (!preventSecondary){ //HOOK: On Move Hit } } } } if (!user->IsFainted()) HOOK(OnAfterHits, userSources, &attack, target); } }