#include "TurnHandler.hpp" #include "../Models/Battle.hpp" #include "../Models/ExecutingAttack.hpp" #include "../../Core/Exceptions/NotImplementedException.hpp" void CreatureLib::Battling::TurnHandler::RunTurn(CreatureLib::Battling::ChoiceQueue &queue) { //HOOK: On Before Turn hook for all choices while (queue.HasNext()){ auto item = queue.Dequeue(); ExecuteChoice(item); delete item; } } void CreatureLib::Battling::TurnHandler::ExecuteChoice(const CreatureLib::Battling::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: return; case TurnChoiceKind::Attack: return ExecuteAttackChoice(dynamic_cast(choice)); case TurnChoiceKind::Item: case TurnChoiceKind::Switch: case TurnChoiceKind::RunAway: throw NotImplementedException(); } } void CreatureLib::Battling::TurnHandler::ExecuteAttackChoice(const CreatureLib::Battling::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 CreatureLib::Battling::TurnHandler::HandleAttackForTarget(CreatureLib::Battling::ExecutingAttack &attack, CreatureLib::Battling::Creature *target, CreatureLib::Battling::ExecutingAttack::TargetData &targetData) { //HOOK: Check if attack fails on target //HOOK: Check if target is invulnerable if (!targetData.IsHit()){ //HOOK: On attack miss. return; } auto numHits = targetData.GetNumberOfHits(); if (numHits == 0) return; auto user = attack.GetUser(); 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); //HOOK: Change move type hit.SetEffectiveness(library->GetTypeLibrary()->GetEffectiveness(hit.GetType(), target->GetTypes())); hit.SetCritical(library->GetCriticalLibrary()->IsCritical(&attack, target, hitIndex)); hit.SetBasePower(dmgLibrary->GetBasePower(&attack, target, hitIndex)); hit.SetDamage(dmgLibrary->GetDamage(&attack, target, hitIndex)); if (attackData->GetCategory() == Library::AttackCategory::Status){ //HOOK: Status attack } else{ auto damage = hit.GetDamage(); if (damage > target->GetCurrentHealth()){ damage = target->GetCurrentHealth(); hit.SetDamage(damage); } if (damage > 0){ target->Damage(damage, DamageSource::AttackDamage); //HOOK: Prevent secondary effects //HOOK: On Move Hit } } } if (!user->IsFainted()){ //HOOK: On After Hits } }