From 56d6e8d6d32c08faf3dbc68f8947443ef72e999c Mon Sep 17 00:00:00 2001 From: Dino Suvalic <82914521+MakoInfused@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:38:40 -0500 Subject: [PATCH] Implement Maniacs Command 3009: ControlBattle --- src/game_battle.cpp | 17 +++++ src/game_battle.h | 10 +++ src/game_battlealgorithm.cpp | 111 ++++++++++++++++++++++++++++++++ src/game_battlealgorithm.h | 52 +++++++++++++++ src/game_battler.cpp | 11 ++++ src/game_battler.h | 7 ++ src/game_interpreter_battle.cpp | 111 +++++++++++++++++++++++++++++++- src/game_interpreter_battle.h | 22 +++++++ src/game_party_base.cpp | 15 +++++ src/game_party_base.h | 7 ++ src/scene_battle_rpg2k3.cpp | 60 +++++++++++++++-- src/scene_battle_rpg2k3.h | 7 +- 12 files changed, 421 insertions(+), 9 deletions(-) diff --git a/src/game_battle.cpp b/src/game_battle.cpp index 8703c704ef..a9cbb8670a 100644 --- a/src/game_battle.cpp +++ b/src/game_battle.cpp @@ -219,11 +219,28 @@ void Game_Battle::UpdateAtbGauges() { const auto multiplier = std::max(1.0, static_cast(275000 - cur_atb) / 55000.0); increment = Utils::RoundTo(multiplier * increment); } + + ManiacBattleHook( + Game_Interpreter_Battle::AtbIncrement, + bat->GetType() == Game_Battler::Type_Enemy, + bat->GetPartyIndex(), + bat->GetAtbGauge(), + increment + ); + bat->IncrementAtbGauge(increment); } } } +bool Game_Battle::ManiacBattleHook(Game_Interpreter_Battle::ManiacBattleHookType hook_type, int var1, int var2, int var3, int var4, int var5, int var6) { + return interpreter->ManiacBattleHook(hook_type, var1, var2, var3, var4, var5, var6); +} + +bool Game_Battle::ManiacProcessSubEvents() { + return interpreter->ProcessManiacSubEvents(); +} + void Game_Battle::ChangeBackground(const std::string& name) { background_name = name; } diff --git a/src/game_battle.h b/src/game_battle.h index 6c828df4ef..7eeece4b71 100644 --- a/src/game_battle.h +++ b/src/game_battle.h @@ -23,6 +23,7 @@ #include #include #include "teleport_target.h" +#include "game_interpreter_battle.h" #include "utils.h" #include "point.h" @@ -109,6 +110,15 @@ namespace Game_Battle { */ void UpdateAtbGauges(); + /** + * Convenience function to call a maniacs battle hook, which processes sub-events at any time. + */ + bool ManiacBattleHook(Game_Interpreter_Battle::ManiacBattleHookType hook_type, int var1, int var2, int var3, int var4 = 0, int var5 = 0, int var6 = 0); + /** + * Convenience function to process all maniacs sub-events, and return whether they're currently running + */ + bool ManiacProcessSubEvents(); + void ChangeBackground(const std::string& name); const std::string& GetBackground(); diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 3c939b64e4..f952aa210b 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -88,6 +88,14 @@ Game_BattleAlgorithm::AlgorithmBase::AlgorithmBase(Type ty, Game_Battler* source party_target = target; } +int Game_BattleAlgorithm::AlgorithmBase::GetActionType() { + return 0; +} + +int Game_BattleAlgorithm::AlgorithmBase::GetActionId() { + return 0; +} + void Game_BattleAlgorithm::AlgorithmBase::Reset() { hp = 0; sp = 0; @@ -185,7 +193,18 @@ int Game_BattleAlgorithm::AlgorithmBase::ApplySpEffect() { // Only absorb the sp that were left source->ChangeSp(-sp); } + + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::StatChange, + target->GetType() == Game_Battler::Type_Enemy, + target->GetPartyIndex(), + target->GetDisplayX(), + target->GetDisplayY(), + 3, + sp + ); } + return sp; } @@ -198,6 +217,16 @@ int Game_BattleAlgorithm::AlgorithmBase::ApplyAtkEffect() { if (IsAbsorbAtk()) { source->ChangeAtkModifier(-atk); } + + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::StatChange, + target->GetType() == Game_Battler::Type_Enemy, + target->GetPartyIndex(), + target->GetDisplayX(), + target->GetDisplayY(), + 4, + atk + ); } return atk; } @@ -211,6 +240,16 @@ int Game_BattleAlgorithm::AlgorithmBase::ApplyDefEffect() { if (IsAbsorbDef()) { source->ChangeDefModifier(-def); } + + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::StatChange, + target->GetType() == Game_Battler::Type_Enemy, + target->GetPartyIndex(), + target->GetDisplayX(), + target->GetDisplayY(), + 5, + def + ); } return def; } @@ -224,6 +263,16 @@ int Game_BattleAlgorithm::AlgorithmBase::ApplySpiEffect() { if (IsAbsorbSpi()) { source->ChangeSpiModifier(-spi); } + + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::StatChange, + target->GetType() == Game_Battler::Type_Enemy, + target->GetPartyIndex(), + target->GetDisplayX(), + target->GetDisplayY(), + 6, + spi + ); } return spi; } @@ -237,6 +286,16 @@ int Game_BattleAlgorithm::AlgorithmBase::ApplyAgiEffect() { if (IsAbsorbAgi()) { source->ChangeAgiModifier(-agi); } + + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::StatChange, + target->GetType() == Game_Battler::Type_Enemy, + target->GetPartyIndex(), + target->GetDisplayX(), + target->GetDisplayY(), + 7, + agi + ); } return agi; } @@ -532,6 +591,10 @@ AlgorithmBase(Type::None, source, source) { // no-op } +int Game_BattleAlgorithm::None::GetActionId() { + return 7; +} + Game_BattleAlgorithm::Normal::Normal(Game_Battler* source, Game_Battler* target, int hits_multiplier, Style style) : AlgorithmBase(Type::Normal, source, target), hits_multiplier(hits_multiplier) { @@ -849,6 +912,14 @@ Game_BattleAlgorithm::Skill::Skill(Game_Battler* source, const lcf::rpg::Skill& { } +int Game_BattleAlgorithm::Skill::GetActionType() { + return 1; +} + +int Game_BattleAlgorithm::Skill::GetActionId() { + return skill.ID; +} + void Game_BattleAlgorithm::Skill::Init() { } @@ -1236,6 +1307,14 @@ Game_BattleAlgorithm::Item::Item(Game_Battler* source, Game_Party_Base* target, // no-op } +int Game_BattleAlgorithm::Item::GetActionType() { + return 3; +} + +int Game_BattleAlgorithm::Item::GetActionId() { + return item.ID; +} + bool Game_BattleAlgorithm::Item::vStart() { Main_Data::game_party->ConsumeItemUse(item.ID); return true; @@ -1340,6 +1419,10 @@ Game_BattleAlgorithm::Defend::Defend(Game_Battler* source) : source->SetIsDefending(true); } +int Game_BattleAlgorithm::Defend::GetActionId() { + return 2; +} + std::string Game_BattleAlgorithm::Defend::GetStartMessage(int line) const { if (line == 0) { if (Feature::HasRpg2kBattleSystem()) { @@ -1360,6 +1443,10 @@ AlgorithmBase(Type::Observe, source, source) { // no-op } +int Game_BattleAlgorithm::Observe::GetActionId() { + return 3; +} + std::string Game_BattleAlgorithm::Observe::GetStartMessage(int line) const { if (line == 0) { if (Feature::HasRpg2kBattleSystem()) { @@ -1376,6 +1463,10 @@ AlgorithmBase(Type::Charge, source, source) { // no-op } +int Game_BattleAlgorithm::Charge::GetActionId() { + return 4; +} + std::string Game_BattleAlgorithm::Charge::GetStartMessage(int line) const { if (line == 0) { if (Feature::HasRpg2kBattleSystem()) { @@ -1396,6 +1487,10 @@ AlgorithmBase(Type::SelfDestruct, source, target) { // no-op } +int Game_BattleAlgorithm::SelfDestruct::GetActionId() { + return 5; +} + std::string Game_BattleAlgorithm::SelfDestruct::GetStartMessage(int line) const { if (line == 0) { if (Feature::HasRpg2kBattleSystem()) { @@ -1450,6 +1545,10 @@ Game_BattleAlgorithm::Escape::Escape(Game_Battler* source) : // no-op } +int Game_BattleAlgorithm::Escape::GetActionId() { + return 6; +} + std::string Game_BattleAlgorithm::Escape::GetStartMessage(int line) const { if (line == 0) { if (Feature::HasRpg2kBattleSystem()) { @@ -1487,6 +1586,14 @@ AlgorithmBase(Type::Transform, source, source), new_monster_id(new_monster_id) { // no-op } +int Game_BattleAlgorithm::Transform::GetActionType() { + return 2; +} + +int Game_BattleAlgorithm::Transform::GetActionId() { + return new_monster_id; +} + std::string Game_BattleAlgorithm::Transform::GetStartMessage(int line) const { if (line == 0 && Feature::HasRpg2kBattleSystem()) { auto* enemy = lcf::ReaderUtil::GetElement(lcf::Data::enemies, new_monster_id); @@ -1509,3 +1616,7 @@ AlgorithmBase(Type::DoNothing, source, source) { // no-op } +int Game_BattleAlgorithm::DoNothing::GetActionId() { + return 7; +} + diff --git a/src/game_battlealgorithm.h b/src/game_battlealgorithm.h index 542e29a905..dacc61b14c 100644 --- a/src/game_battlealgorithm.h +++ b/src/game_battlealgorithm.h @@ -83,6 +83,19 @@ class AlgorithmBase { public: virtual ~AlgorithmBase() {} + enum ActionType { + Basic, + Skill, + Transformation, + Item + }; + + /** @return the category associated with this action */ + virtual int GetActionType(); + + /** @return the unique identifier associated with this specific action */ + virtual int GetActionId(); + /** @return the source of the battle action. */ Game_Battler* GetSource() const; @@ -616,6 +629,9 @@ class AlgorithmBase { class None : public AlgorithmBase { public: None(Game_Battler* source); + + /** @return the id associated with this action */ + int GetActionId() override; }; @@ -667,6 +683,12 @@ class Skill : public AlgorithmBase { Skill(Game_Battler* source, Game_Party_Base* target, const lcf::rpg::Skill& skill, const lcf::rpg::Item* item = NULL); Skill(Game_Battler* source, const lcf::rpg::Skill& skill, const lcf::rpg::Item* item = NULL); + /** @return the type associated with this action */ + int GetActionType() override; + + /** @return the id associated with this action */ + int GetActionId() override; + bool IsTargetValid(const Game_Battler&) const override; bool vExecute() override; bool vStart() override; @@ -704,6 +726,12 @@ class Item : public AlgorithmBase { Item(Game_Battler* source, Game_Party_Base* target, const lcf::rpg::Item& item); Item(Game_Battler* source, const lcf::rpg::Item& item); + /** @return the type associated with this action */ + int GetActionType() override; + + /** @return the id associated with this action */ + int GetActionId() override; + bool IsTargetValid(const Game_Battler&) const override; bool vExecute() override; bool vStart() override; @@ -724,6 +752,9 @@ class Defend : public AlgorithmBase { public: Defend(Game_Battler* source); + /** @return the id associated with this action */ + int GetActionId() override; + std::string GetStartMessage(int line) const override; int GetSourcePose() const override; }; @@ -732,6 +763,9 @@ class Observe : public AlgorithmBase { public: Observe(Game_Battler* source); + /** @return the id associated with this action */ + int GetActionId() override; + std::string GetStartMessage(int line) const override; }; @@ -739,6 +773,9 @@ class Charge : public AlgorithmBase { public: Charge(Game_Battler* source); + /** @return the id associated with this action */ + int GetActionId() override; + std::string GetStartMessage(int line) const override; void ApplyCustomEffect() override; }; @@ -747,6 +784,9 @@ class SelfDestruct : public AlgorithmBase { public: SelfDestruct(Game_Battler* source, Game_Party_Base* target); + /** @return the id associated with this action */ + int GetActionId() override; + std::string GetStartMessage(int line) const override; const lcf::rpg::Sound* GetStartSe() const override; bool vExecute() override; @@ -759,6 +799,9 @@ class Escape : public AlgorithmBase { public: Escape(Game_Battler* source); + /** @return the id associated with this action */ + int GetActionId() override; + std::string GetStartMessage(int line) const override; int GetSourcePose() const override; const lcf::rpg::Sound* GetStartSe() const override; @@ -769,6 +812,12 @@ class Transform : public AlgorithmBase { public: Transform(Game_Battler* source, int new_monster_id); + /** @return the type associated with this action */ + int GetActionType() override; + + /** @return the id associated with this action */ + int GetActionId() override; + std::string GetStartMessage(int line) const override; void ApplyCustomEffect() override; @@ -780,6 +829,9 @@ class Transform : public AlgorithmBase { class DoNothing : public AlgorithmBase { public: DoNothing(Game_Battler* source); + + /** @return the id associated with this action */ + int GetActionId() override; }; inline Type AlgorithmBase::GetType() const { diff --git a/src/game_battler.cpp b/src/game_battler.cpp index 03eafdb57a..671b611d7f 100644 --- a/src/game_battler.cpp +++ b/src/game_battler.cpp @@ -367,6 +367,13 @@ bool Game_Battler::AddState(int state_id, bool allow_battle_states) { } } + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::SetState, + GetType() == Game_Battler::Type_Enemy, + GetPartyIndex(), + state_id + ); + return was_added; } @@ -586,6 +593,10 @@ Game_Party_Base& Game_Battler::GetParty() const { } } +int Game_Battler::GetPartyIndex() { + return GetParty().GetMemberIndex(this); +} + void Game_Battler::UpdateBattle() { Shake::Update(shake.position, shake.time_left, shake.strength, shake.speed, false); Flash::Update(flash.current_level, flash.time_left); diff --git a/src/game_battler.h b/src/game_battler.h index 48ce45d179..79b4acad7c 100644 --- a/src/game_battler.h +++ b/src/game_battler.h @@ -769,6 +769,13 @@ class Game_Battler { */ Game_Party_Base& GetParty() const; + /** + * Convenience function to access the battlers party member index. + * + * @return Index of this member in their party. + */ + int GetPartyIndex(); + /** * Gets the maximal atb gauge value. * When GetAtbGauge() >= this, the battler can act. diff --git a/src/game_interpreter_battle.cpp b/src/game_interpreter_battle.cpp index 8fc9a65495..3d1ac489ae 100644 --- a/src/game_interpreter_battle.cpp +++ b/src/game_interpreter_battle.cpp @@ -43,14 +43,35 @@ enum TargetType { Enemy, }; +// Implemented as a static map, since maniac hooks can only have one common event callback at a time. +// Subsequent calls will simply override the previous common event callback. +std::map> Game_Interpreter_Battle::maniac_hooks = { + {AtbIncrement, std::make_tuple(0, 0)}, + {DamagePop, std::make_tuple(0, 0)}, + {Targetting, std::make_tuple(0, 0)}, + {SetState, std::make_tuple(0, 0)}, + {StatChange, std::make_tuple(0, 0)} +}; + static const char* target_text[] = { "actor", "party member", "enemy" }; static const void MissingTargetWarning(const char* command_name, TargetType target_type, int target_id) { Output::Warning("{}: Invalid {} ID: {}", command_name, target_text[target_type], target_id); } + +// Provides a facility for battle sub-events to be run immediately +// without blocking the standard interpreter from actually processing them. +std::unique_ptr maniac_interpreter; + Game_Interpreter_Battle::Game_Interpreter_Battle(Span pages) : Game_Interpreter(true), pages(pages), executed(pages.size(), false) +{ + maniac_interpreter.reset(new Game_Interpreter_Battle()); +} + +Game_Interpreter_Battle::Game_Interpreter_Battle() + : Game_Interpreter(true) { } @@ -600,12 +621,98 @@ bool Game_Interpreter_Battle::CommandEndBranchBattle(lcf::rpg::EventCommand cons return true; } -bool Game_Interpreter_Battle::CommandManiacControlBattle(lcf::rpg::EventCommand const&) { +bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, int var1, int var2, int var3, int var4, int var5, int var6) { + if (!Player::IsPatchManiac()) { + return false; + } + + int common_event_id = std::get<0>(maniac_hooks[hook_type]); + int variable_start_id = std::get<1>(maniac_hooks[hook_type]); + + if (common_event_id <= 0) { + return false; + } + + Game_CommonEvent* common_event = lcf::ReaderUtil::GetElement(Game_Map::GetCommonEvents(), common_event_id); + if (!common_event) { + Output::Warning("CommandManiacControlBattle: Can't call invalid common event {}", common_event_id); + return false; + } + + // pushes the common event to be run into the queue of events. + maniac_interpreter->Push(common_event); + + // pushes the change variable events into the interpreters + // event queue, so we don't run into a race condition. + std::vector pre_commands; + for (size_t i = 0; i < 6; i++) + { + auto event_command = lcf::rpg::EventCommand(); + event_command.code = static_cast(lcf::rpg::EventCommand::Code::ControlVars); + event_command.parameters = lcf::DBArray(7); + event_command.parameters[1] = variable_start_id + i; + switch (i) + { + case 0: + event_command.parameters[5] = var1; + break; + case 1: + event_command.parameters[5] = var2; + break; + case 2: + event_command.parameters[5] = var3; + break; + case 3: + event_command.parameters[5] = var4; + break; + case 4: + event_command.parameters[5] = var5; + break; + case 5: + event_command.parameters[5] = var6; + break; + default: + break; + } + pre_commands.push_back(event_command); + } + + // Push is actually "push_back", so this gets added before other events. + maniac_interpreter->Push(pre_commands, 0); + + // Necessary to start the sub-event. + maniac_interpreter->Update(); + + return true; +} + +bool Game_Interpreter_Battle::ProcessManiacSubEvents() { + // If we have sub-events we're going to update them immediately + // until the queue is empty while making the rest of the game wait. + if (Player::IsPatchManiac() && maniac_interpreter->IsRunning()) { + maniac_interpreter->Update(); + return true; + } + return false; +} + +bool Game_Interpreter_Battle::CommandManiacControlBattle(lcf::rpg::EventCommand const& com) { if (!Player::IsPatchManiac()) { return true; } - Output::Warning("Maniac Patch: Command ControlBattle not supported"); + ManiacBattleHookType control_type_flags = static_cast(com.parameters[0]); + int common_event_flags = com.parameters[1]; + int common_event_identifier = com.parameters[2]; + int value_reference_identifier = com.parameters[3]; + + int common_event_id = ValueOrVariable(common_event_flags, common_event_identifier); + + // Sets the maniacs battle event hook to: + // the common event id and the variable id the developer would like to use. + std::get<0>(maniac_hooks[control_type_flags]) = common_event_identifier; + std::get<1>(maniac_hooks[control_type_flags]) = value_reference_identifier; + return true; } diff --git a/src/game_interpreter_battle.h b/src/game_interpreter_battle.h index 96ec7d191f..0fff228ef3 100644 --- a/src/game_interpreter_battle.h +++ b/src/game_interpreter_battle.h @@ -40,6 +40,7 @@ class Game_Interpreter_Battle : public Game_Interpreter { public: explicit Game_Interpreter_Battle(Span pages); + explicit Game_Interpreter_Battle(); int GetNumPages() const; @@ -60,6 +61,26 @@ class Game_Interpreter_Battle : public Game_Interpreter bool ExecuteCommand(lcf::rpg::EventCommand const& com) override; + /** + * All possible hook type events that maniacs offers. + */ + enum ManiacBattleHookType { + AtbIncrement, + DamagePop, + Targetting, + SetState, + StatChange + }; + + /** + * Calls a maniacs battle hook, which processes sub-events at any time. + */ + bool ManiacBattleHook(ManiacBattleHookType hook_type, int var1, int var2, int var3, int var4 = 0, int var5 = 0, int var6 = 0); + /** + * Processes all maniacs sub-events, and returns whether it's currently running + */ + bool ProcessManiacSubEvents(); + private: bool CommandCallCommonEvent(lcf::rpg::EventCommand const& com); bool CommandForceFlee(lcf::rpg::EventCommand const& com); @@ -87,6 +108,7 @@ class Game_Interpreter_Battle : public Game_Interpreter int current_actor_id = 0; bool targets_single_enemy = false; bool force_flee_enabled = false; + static std::map> maniac_hooks; }; inline int Game_Interpreter_Battle::GetNumPages() const { diff --git a/src/game_party_base.cpp b/src/game_party_base.cpp index 88c11db579..4709070bb1 100644 --- a/src/game_party_base.cpp +++ b/src/game_party_base.cpp @@ -119,3 +119,18 @@ int Game_Party_Base::GetAverageAgility() { return battlers.empty() ? 1 : agi /= battlers.size(); } +int Game_Party_Base::GetMemberIndex(Game_Battler* battler) { + std::vector battlers; + GetBattlers(battlers); + + auto iterator = std::find(battlers.begin(), battlers.end(), battler); + + if (iterator != battlers.end()) { + int index = std::distance(battlers.begin(), iterator); + return index; + } + else { + return -1; + } +} + diff --git a/src/game_party_base.h b/src/game_party_base.h index 1834e9a2e9..cdd1457066 100644 --- a/src/game_party_base.h +++ b/src/game_party_base.h @@ -108,6 +108,13 @@ class Game_Party_Base { */ int GetAverageAgility(); + /** + * Gets the index of the battler in the party + * + * @return the battlers party index, or -1 if not in the party + */ + int GetMemberIndex(Game_Battler* battler); + private: }; diff --git a/src/scene_battle_rpg2k3.cpp b/src/scene_battle_rpg2k3.cpp index aeb3ea65a4..b4ad096021 100644 --- a/src/scene_battle_rpg2k3.cpp +++ b/src/scene_battle_rpg2k3.cpp @@ -498,7 +498,24 @@ void Scene_Battle_Rpg2k3::UpdateAnimations() { } } -void Scene_Battle_Rpg2k3::DrawFloatText(int x, int y, int color, StringView text) { +void Scene_Battle_Rpg2k3::DrawFloatText(int x, int y, int color, StringView text, Game_Battler* battler, FloatTextType type) { + std::stringstream ss(text.to_string()); + int value = 0; + ss >> value; + bool should_override = Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::DamagePop, + battler->GetType() == Game_Battler::Type_Enemy, + battler->GetPartyIndex(), + x, + y, + static_cast(type), + value + ); + + if (should_override) { + return; + } + Rect rect = Text::GetSize(*Font::Default(), text); BitmapRef graphic = Bitmap::Create(rect.width, rect.height); @@ -953,6 +970,12 @@ void Scene_Battle_Rpg2k3::vUpdate() { break; } + // this is checked separately because we want normal events to be processed + // just not sub-events called by maniacs battle hooks. + if (state != State_Victory && state != State_Defeat && Game_Battle::ManiacProcessSubEvents()) { + break; + } + if (!CheckWait()) { break; } @@ -2209,7 +2232,9 @@ Scene_Battle_Rpg2k3::BattleActionReturn Scene_Battle_Rpg2k3::ProcessBattleAction b->GetBattlePosition().x, b->GetBattlePosition().y, damageTaken < 0 ? Font::ColorDefault : Font::ColorHeal, - std::to_string(std::abs(damageTaken))); + std::to_string(std::abs(damageTaken)), + b, + damageTaken < 0 ? Damage : Heal); } if (b->GetType() == Game_Battler::Type_Ally) { auto* sprite = static_cast(b)->GetActorBattleSprite(); @@ -2632,14 +2657,18 @@ Scene_Battle_Rpg2k3::BattleActionReturn Scene_Battle_Rpg2k3::ProcessBattleAction target->GetBattlePosition().x, target->GetBattlePosition().y, hp > 0 ? Font::ColorHeal : Font::ColorDefault, - std::to_string(std::abs(hp))); + std::to_string(std::abs(hp)), + target, + hp > 0 ? Heal : Damage); if (action->IsAbsorbHp()) { DrawFloatText( source->GetBattlePosition().x, source->GetBattlePosition().y, hp > 0 ? Font::ColorDefault : Font::ColorHeal, - std::to_string(std::abs(hp))); + std::to_string(std::abs(hp)), + source, + hp > 0 ? Damage : Heal); } } @@ -2660,7 +2689,9 @@ Scene_Battle_Rpg2k3::BattleActionReturn Scene_Battle_Rpg2k3::ProcessBattleAction target->GetBattlePosition().x, target->GetBattlePosition().y, 0, - lcf::Data::terms.miss); + lcf::Data::terms.miss, + target, + Miss); } status_window->Refresh(); @@ -2821,6 +2852,21 @@ void Scene_Battle_Rpg2k3::RowSelected() { } void Scene_Battle_Rpg2k3::ActionSelectedCallback(Game_Battler* for_battler) { + auto single_target = for_battler->GetBattleAlgorithm()->GetOriginalSingleTarget(); + auto group_targets = for_battler->GetBattleAlgorithm()->GetOriginalPartyTarget(); + // Target: 0 None, 1 Single Enemy, 2 All Enemies, 3 Single Ally, 4 All Allies + Game_Battle::ManiacBattleHook( + Game_Interpreter_Battle::Targetting, + for_battler->GetType() == Game_Battler::Type_Enemy, + for_battler->GetPartyIndex(), + for_battler->GetBattleAlgorithm()->GetActionType(), + for_battler->GetBattleAlgorithm()->GetActionId(), + single_target + ? (single_target->GetType() != Game_Battler::Type_Enemy ? 1 : 3) + : (group_targets->GetRandomActiveBattler()->GetType() != Game_Battler::Type_Enemy ? 2 : 4), + single_target ? single_target->GetPartyIndex() : 0 + ); + for_battler->SetAtbGauge(0); if (for_battler == active_actor) { @@ -2905,7 +2951,9 @@ void Scene_Battle_Rpg2k3::OnEventHpChanged(Game_Battler* battler, int hp) { battler->GetBattlePosition().x, battler->GetBattlePosition().y, hp < 0 ? Font::ColorDefault : Font::ColorHeal, - std::to_string(std::abs(hp))); + std::to_string(std::abs(hp)), + battler, + hp < 0 ? Damage : Heal); } void Scene_Battle_Rpg2k3::RecreateSpWindow(Game_Battler* battler) { diff --git a/src/scene_battle_rpg2k3.h b/src/scene_battle_rpg2k3.h index d411cd309a..c88ab4d882 100644 --- a/src/scene_battle_rpg2k3.h +++ b/src/scene_battle_rpg2k3.h @@ -123,7 +123,12 @@ class Scene_Battle_Rpg2k3 : public Scene_Battle { void RefreshCommandWindow(const Game_Actor* actor); void SetActiveActor(int idx); - void DrawFloatText(int x, int y, int color, StringView text); + enum FloatTextType { + Damage = 0, + Heal = 1, + Miss = 2, + }; + void DrawFloatText(int x, int y, int color, StringView text, Game_Battler* battler, FloatTextType type); bool IsTransparent() const;