/*
* 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();
}