/* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with EasyRPG Player. If not, see . */ // Headers #include #include "bitmap.h" #include "cache.h" #include "input.h" #include "game_enemyparty.h" #include "game_party.h" #include "game_actor.h" #include "game_system.h" #include "game_battle.h" #include "player.h" #include "font.h" #include "output.h" #include "window_battlestatus.h" Window_BattleStatus::Window_BattleStatus(int ix, int iy, int iwidth, int iheight, bool enemy) : Window_Selectable(ix, iy, iwidth, iheight), mode(ChoiceMode_All), enemy(enemy) { SetBorderX(4); SetContents(Bitmap::Create(width - 8, height - 16)); if (Player::IsRPG2k3() && lcf::Data::battlecommands.window_size == lcf::rpg::BattleCommands::WindowSize_small) { height = 68; menu_item_height = 14; actor_face_height = 17; SetBorderY(5); SetContents(Bitmap::Create(width - 8, height - 10)); } index = -1; if (lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_gauge) { // Simulate a borderless window // Doing it this way for gauge style makes the implementation on // scene-side easier border_x = 0; border_y = 0; SetContents(Bitmap::Create(width, height)); SetOpacity(0); } Refresh(); } void Window_BattleStatus::Refresh() { contents->Clear(); if (enemy) { item_max = Main_Data::game_enemyparty->GetBattlerCount(); } else { item_max = Main_Data::game_party->GetBattlerCount(); } item_max = std::min(item_max, 4); for (int i = 0; i < item_max; i++) { // The party only contains valid battlers const Game_Battler* actor; if (enemy) { actor = &(*Main_Data::game_enemyparty)[i]; } else { actor = &(*Main_Data::game_party)[i]; } if (!enemy && lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_gauge) { DrawActorFace(*static_cast(actor), 80 * i, actor_face_height); } else { int y = menu_item_height / 8 + i * menu_item_height; DrawActorName(*actor, 4, y); if (Player::IsRPG2k()) { int hpdigits = (actor->MaxHpValue() >= 1000) ? 4 : 3; int spdigits = (actor->MaxSpValue() >= 1000) ? 4 : 3; DrawActorState(*actor, (hpdigits < 4 && spdigits < 4) ? 86 : 80, y); DrawActorHp(*actor, 178 - hpdigits * 6 - spdigits * 6, y, hpdigits, true); DrawActorSp(*actor, 220 - spdigits * 6, y, spdigits, false); } else { if (lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_traditional) { DrawActorState(*actor, 84, y); DrawActorHpValue(*actor, 136 + 4 * 6, y); } else { DrawActorState(*actor, 80, y); } } } } RefreshGauge(); } void Window_BattleStatus::RefreshGauge() { if (Player::IsRPG2k3()) { if (lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_alternative) { if (lcf::Data::battlecommands.window_size == lcf::rpg::BattleCommands::WindowSize_small) { contents->ClearRect(Rect(192, 0, 45, 58)); } else { contents->ClearRect(Rect(192, 0, 45, 64)); } } for (int i = 0; i < item_max; ++i) { // The party always contains valid battlers Game_Battler* actor; if (enemy) { actor = &(*Main_Data::game_enemyparty)[i]; } else { actor = &(*Main_Data::game_party)[i]; } if (!enemy && lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_gauge) { BitmapRef system2 = Cache::System2(); if (system2) { // Clear number and gauge drawing area contents->ClearRect(Rect(40 + 80 * i, actor_face_height, 8 * 4, 48)); // Number clearing removed part of the face, but both, clear and redraw // are needed because some games don't have face graphics that are huge enough // to clear the number area (e.g. Ara Fell) DrawActorFace(*static_cast(actor), 80 * i, actor_face_height); int x = 32 + i * 80; int y = actor_face_height; // Left Gauge contents->Blit(x, y, *system2, Rect(0, 32, 16, 48), Opacity::Opaque()); x += 16; // Center const auto fill_x = x; contents->StretchBlit(Rect(x, y, 25, 48), *system2, Rect(16, 32, 16, 48), Opacity::Opaque()); x += 25; // Right contents->Blit(x, y, *system2, Rect(32, 32, 16, 48), Opacity::Opaque()); // HP DrawGaugeSystem2(fill_x, y, actor->GetHp(), actor->GetMaxHp(), 0); // SP DrawGaugeSystem2(fill_x, y + 16, actor->GetSp(), actor->GetMaxSp(), 1); // Gauge DrawGaugeSystem2(fill_x, y + 16 * 2, actor->GetAtbGauge(), actor->GetMaxAtbGauge(), 2); // Numbers x = 40 + 80 * i; DrawNumberSystem2(x, y, actor->GetHp()); DrawNumberSystem2(x, y + 12 + 4, actor->GetSp()); } } else { int y = menu_item_height / 8 + i * menu_item_height; if (lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_alternative) { // RPG_RT Bug (?): Gauge hidden when selected due to transparency (wrong color when rendering) if (lcf::Data::battlecommands.transparency == lcf::rpg::BattleCommands::Transparency_opaque || (menu_item_height / 8 + index * menu_item_height != y)) { DrawGauge(*actor, 202 - 10, y - 2, lcf::Data::battlecommands.transparency == lcf::rpg::BattleCommands::Transparency_opaque ? 96 : 255); } int hpdigits = (actor->MaxHpValue() >= 1000) ? 4 : 3; int spdigits = (actor->MaxSpValue() >= 1000) ? 4 : 3; DrawActorHp(*actor, 178 - hpdigits * 6 - spdigits * 6, y, hpdigits, true); DrawActorSp(*actor, 220 - spdigits * 6, y, spdigits, false); } else { DrawGauge(*actor, 156, y - 2); } } } } } void Window_BattleStatus::DrawGaugeSystem2(int x, int y, int cur_value, int max_value, int which) { BitmapRef system2 = Cache::System2(); assert(system2); if (max_value == 0) { return; } int gauge_x; if (cur_value == max_value) { gauge_x = 16; } else { gauge_x = 0; } int gauge_width = 25; if (max_value > 0) { gauge_width = 25 * cur_value / max_value; } contents->StretchBlit(Rect(x, y, gauge_width, 16), *system2, Rect(48 + gauge_x, 32 + 16 * which, 16, 16), Opacity::Opaque()); } void Window_BattleStatus::DrawNumberSystem2(int x, int y, int value) { BitmapRef system2 = Cache::System2(); assert(system2); bool handle_zero = false; if (value >= 1000) { contents->Blit(x, y, *system2, Rect((value / 1000) * 8, 80, 8, 16), Opacity::Opaque()); value %= 1000; if (value < 100) { handle_zero = true; } } if (handle_zero || value >= 100) { handle_zero = false; contents->Blit(x + 8, y, *system2, Rect((value / 100) * 8, 80, 8, 16), Opacity::Opaque()); value %= 100; if (value < 10) { handle_zero = true; } } if (handle_zero || value >= 10) { contents->Blit(x + 8 * 2, y, *system2, Rect((value / 10) * 8, 80, 8, 16), Opacity::Opaque()); value %= 10; } contents->Blit(x + 8 * 3, y, *system2, Rect(value * 8, 80, 8, 16), Opacity::Opaque()); } int Window_BattleStatus::ChooseActiveCharacter() { int old_index = index < 0 ? 0 : index; index = -1; for (int i = 0; i < item_max; i++) { int new_index = (old_index + i) % item_max; if ((*Main_Data::game_party)[new_index].IsAtbGaugeFull()) { index = new_index; return index; } } if (index != old_index) UpdateCursorRect(); return index; } void Window_BattleStatus::SetChoiceMode(ChoiceMode new_mode) { mode = new_mode; } void Window_BattleStatus::Update() { // Window Selectable update logic skipped on purpose // (breaks up/down-logic) Window_Base::Update(); int old_item_max = item_max; if (enemy) { item_max = Main_Data::game_enemyparty->GetBattlerCount(); } else { item_max = Main_Data::game_party->GetBattlerCount(); } if (item_max != old_item_max) { Refresh(); } else if (Player::IsRPG2k3()) { RefreshGauge(); } if (active && index >= 0) { if (Input::IsRepeated(Input::DOWN) || Input::IsTriggered(Input::SCROLL_DOWN)) { Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor)); for (int i = 1; i < item_max; i++) { int new_index = (index + i) % item_max; if (IsChoiceValid((*Main_Data::game_party)[new_index])) { index = new_index; break; } } } if (Input::IsRepeated(Input::UP) || Input::IsTriggered(Input::SCROLL_UP)) { Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor)); for (int i = item_max - 1; i > 0; i--) { int new_index = (index + i) % item_max; if (IsChoiceValid((*Main_Data::game_party)[new_index])) { index = new_index; break; } } } } UpdateCursorRect(); } void Window_BattleStatus::UpdateCursorRect() { if (lcf::Data::battlecommands.battle_type == lcf::rpg::BattleCommands::BattleType_gauge) { SetCursorRect(Rect()); return; } if (index < 0) SetCursorRect(Rect()); else SetCursorRect(Rect(0, index * menu_item_height, contents->GetWidth(), menu_item_height)); } bool Window_BattleStatus::IsChoiceValid(const Game_Battler& battler) const { switch (mode) { case ChoiceMode_All: return true; case ChoiceMode_Alive: return !battler.IsDead(); case ChoiceMode_Dead: return battler.IsDead(); case ChoiceMode_Ready: return battler.IsAtbGaugeFull(); case ChoiceMode_None: return false; default: assert(false && "Invalid Choice"); return false; } } void Window_BattleStatus::RefreshActiveFromValid() { std::vector battlers; if (enemy) { Main_Data::game_enemyparty->GetBattlers(battlers); } else { Main_Data::game_party->GetBattlers(battlers); } for (size_t i = 0; i < battlers.size(); ++i) { auto* battler = battlers[i]; if (IsChoiceValid(*battler)) { if (!GetActive() || GetIndex() < 0) { SetIndex(i); SetActive(true); } return; } SetIndex(-1); SetActive(false); } UpdateCursorRect(); }