Files
2025-03-02 19:38:55 +01:00

346 lines
10 KiB
C++

/*
* 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 <http://www.gnu.org/licenses/>.
*/
// Headers
#include <iomanip>
#include <sstream>
#include "window_base.h"
#include "cache.h"
#include <lcf/data.h>
#include "game_system.h"
#include "bitmap.h"
#include "font.h"
#include "player.h"
Window_Base::Window_Base(int x, int y, int width, int height, Drawable::Flags flags)
: Window(flags)
{
SetWindowskin(Cache::SystemOrBlack());
SetX(x);
SetY(y);
SetWidth(width);
SetHeight(height);
SetStretch(Main_Data::game_system->GetMessageStretch() == lcf::rpg::System::Stretch_stretch);
SetZ(Priority_Window);
}
bool Window_Base::InitMovement(int old_x, int old_y, int new_x, int new_y, int duration) {
current_frame = 0;
old_position[0] = old_x;
old_position[1] = old_y;
new_position[0] = new_x;
new_position[1] = new_y;
SetX(old_position[0]);
SetY(old_position[1]);
total_frames = 0;
if (old_x != new_x || old_y != new_y) {
total_frames = duration;
return true;
}
return false;
}
bool Window_Base::IsMovementActive() {
return total_frames > 0 && current_frame <= total_frames;
}
void Window_Base::Update() {
Window::Update();
if (IsSystemGraphicUpdateAllowed()) {
SetWindowskin(Cache::SystemOrBlack());
SetStretch(Main_Data::game_system->GetMessageStretch() == lcf::rpg::System::Stretch_stretch);
}
UpdateMovement();
}
void Window_Base::UpdateMovement() {
if (!IsMovementActive()) {
return;
}
current_frame++;
if (IsMovementActive()) {
SetX(old_position[0] + (new_position[0] - old_position[0]) * current_frame / total_frames);
SetY(old_position[1] + (new_position[1] - old_position[1]) * current_frame / total_frames);
} else {
SetX(new_position[0]);
SetY(new_position[1]);
}
}
void Window_Base::OnFaceReady(FileRequestResult* result, int face_index, int cx, int cy, bool flip) {
BitmapRef faceset = Cache::Faceset(result->file);
Rect src_rect(
(face_index % 4) * 48,
face_index / 4 * 48,
48,
48
);
if (flip) {
contents->FlipBlit(cx, cy, *faceset, src_rect, true, false, Opacity::Opaque());
}
else {
contents->Blit(cx, cy, *faceset, src_rect, 255);
}
}
// All these functions assume that the input is valid
void Window_Base::DrawFace(std::string_view face_name, int face_index, int cx, int cy, bool flip) {
if (face_name.empty()) { return; }
FileRequestAsync* request = AsyncHandler::RequestFile("FaceSet", face_name);
request->SetGraphicFile(true);
face_request_ids.push_back(request->Bind(&Window_Base::OnFaceReady, this, face_index, cx, cy, flip));
request->Start();
}
void Window_Base::DrawActorFace(const Game_Actor& actor, int cx, int cy) {
DrawFace(actor.GetFaceName(), actor.GetFaceIndex(), cx, cy);
}
void Window_Base::DrawActorName(const Game_Battler& actor, int cx, int cy) const {
contents->TextDraw(cx, cy, Font::ColorDefault, actor.GetName());
}
void Window_Base::DrawActorTitle(const Game_Actor& actor, int cx, int cy) const {
contents->TextDraw(cx, cy, Font::ColorDefault, actor.GetTitle());
}
void Window_Base::DrawActorClass(const Game_Actor& actor, int cx, int cy) const {
contents->TextDraw(cx, cy, Font::ColorDefault, actor.GetClassName());
}
void Window_Base::DrawActorLevel(const Game_Actor& actor, int cx, int cy) const {
// Draw LV-String
contents->TextDraw(cx, cy, 1, lcf::Data::terms.lvl_short);
// Draw Level of the Actor
contents->TextDraw(cx + (lcf::Data::system.easyrpg_max_level >= 100 ? 30 : 24), cy, Font::ColorDefault, std::to_string(actor.GetLevel()), Text::AlignRight);
}
void Window_Base::DrawActorState(const Game_Battler& actor, int cx, int cy) const {
// Unit has Normal state if no state is set
const lcf::rpg::State* state = actor.GetSignificantState();
if (!state) {
contents->TextDraw(cx, cy, Font::ColorDefault, lcf::Data::terms.normal_status);
} else {
contents->TextDraw(cx, cy, state->color, state->name);
}
}
void Window_Base::DrawActorExp(const Game_Actor& actor, int cx, int cy) const {
// Draw EXP-String
int width = 7;
if (actor.MaxExpValue() < 1000000) {
width = 6;
contents->TextDraw(cx, cy, 1, lcf::Data::terms.exp_short);
}
// Current Exp of the Actor
// ------/------
std::stringstream ss;
ss << std::setfill(' ') << std::setw(width) << actor.GetExpString();
// Delimiter
ss << '/';
// Exp for Level up
ss << std::setfill(' ') << std::setw(width) << actor.GetNextExpString();
contents->TextDraw(cx + (width == 6 ? 12 : 0), cy, Font::ColorDefault, ss.str(), Text::AlignLeft);
}
void Window_Base::DrawActorHp(const Game_Battler& actor, int cx, int cy, int digits, bool draw_max) const {
// Draw HP-String
contents->TextDraw(cx, cy, 1, lcf::Data::terms.hp_short);
// Draw Current HP of the Actor
cx += 12;
// Color: 0 okay, 4 critical, 5 dead
int color = GetValueFontColor(actor.GetHp(), actor.GetMaxHp(), true);
auto dx = digits * 6;
contents->TextDraw(cx + dx, cy, color, std::to_string(actor.GetHp()), Text::AlignRight);
if (!draw_max)
return;
// Draw the /
cx += dx;
contents->TextDraw(cx, cy, Font::ColorDefault, "/");
// Draw Max Hp
cx += 6;
contents->TextDraw(cx + dx, cy, Font::ColorDefault, std::to_string(actor.GetMaxHp()), Text::AlignRight);
}
void Window_Base::DrawActorSp(const Game_Battler& actor, int cx, int cy, int digits, bool draw_max) const {
// Draw SP-String
contents->TextDraw(cx, cy, 1, lcf::Data::terms.sp_short);
// Draw Current SP of the Actor
cx += 12;
// Color: 0 okay, 4 critical/empty
int color = GetValueFontColor(actor.GetSp(), actor.GetMaxSp(), false);
auto dx = digits * 6;
contents->TextDraw(cx + dx, cy, color, std::to_string(actor.GetSp()), Text::AlignRight);
if (!draw_max)
return;
// Draw the /
cx += dx;
contents->TextDraw(cx, cy, Font::ColorDefault, "/");
// Draw Max Sp
cx += 6;
contents->TextDraw(cx + dx, cy, Font::ColorDefault, std::to_string(actor.GetMaxSp()), Text::AlignRight);
}
void Window_Base::DrawActorParameter(const Game_Battler& actor, int cx, int cy, int type) const {
std::string_view name;
int value;
switch (type) {
case 0:
name = lcf::Data::terms.attack;
value = actor.GetAtk();
break;
case 1:
name = lcf::Data::terms.defense;
value = actor.GetDef();
break;
case 2:
name = lcf::Data::terms.spirit;
value = actor.GetSpi();
break;
case 3:
name = lcf::Data::terms.agility;
value = actor.GetAgi();
break;
default:
return;
}
// Draw Term
contents->TextDraw(cx, cy, 1, name);
// Draw Value
contents->TextDraw(cx + 78, cy, Font::ColorDefault, std::to_string(value), Text::AlignRight);
}
void Window_Base::DrawEquipmentType(const Game_Actor& actor, int cx, int cy, int type) const {
std::string_view name;
switch (type) {
case 0:
name = lcf::Data::terms.weapon;
break;
case 1:
if (actor.HasTwoWeapons()) {
name = lcf::Data::terms.weapon;
} else {
name = lcf::Data::terms.shield;
}
break;
case 2:
name = lcf::Data::terms.armor;
break;
case 3:
name = lcf::Data::terms.helmet;
break;
case 4:
name = lcf::Data::terms.accessory;
break;
default:
return;
}
contents->TextDraw(cx, cy, 1, name);
}
void Window_Base::DrawItemName(const lcf::rpg::Item& item, int cx, int cy, bool enabled) const {
int color = enabled ? Font::ColorDefault : Font::ColorDisabled;
contents->TextDraw(cx, cy, color, item.name);
}
void Window_Base::DrawSkillName(const lcf::rpg::Skill& skill, int cx, int cy, bool enabled) const {
int color = enabled ? Font::ColorDefault : Font::ColorDisabled;
contents->TextDraw(cx, cy, color, skill.name);
}
void Window_Base::DrawCurrencyValue(int money, int cx, int cy) const {
// This function draws right aligned because of the dynamic with of the
// gold output (cx and cy define the right border)
std::stringstream gold;
gold << money;
Rect gold_text_size = Text::GetSize(*Font::Default(), lcf::Data::terms.gold);
contents->TextDraw(cx, cy, 1, lcf::Data::terms.gold, Text::AlignRight);
contents->TextDraw(cx - gold_text_size.width, cy, Font::ColorDefault, gold.str(), Text::AlignRight);
}
void Window_Base::DrawGauge(const Game_Battler& actor, int cx, int cy, int alpha) const {
BitmapRef system2 = Cache::System2();
if (!system2) {
return;
}
bool full = actor.IsAtbGaugeFull();
// Which gauge (0 - 2)
int gauge_y = 32 + 2 * 16;
// Three components of the gauge
Rect gauge_left(0, gauge_y, 16, 16);
Rect gauge_center(16, gauge_y, 16, 16);
Rect gauge_right(32, gauge_y, 16, 16);
Rect dst_rect(cx + 16, cy, 25, 16);
contents->Blit(cx + 0, cy, *system2, gauge_left, alpha);
contents->Blit(cx + 16 + 25, cy, *system2, gauge_right, alpha);
contents->StretchBlit(dst_rect, *system2, gauge_center, alpha);
const auto atb = actor.GetAtbGauge();
const auto gauge_w = 25 * atb / actor.GetMaxAtbGauge();
if (gauge_w > 0) {
// Full or not full bar
Rect gauge_bar(full ? 64 : 48, gauge_y, 16, 16);
Rect bar_rect(cx + 16, cy, gauge_w, 16);
contents->StretchBlit(bar_rect, *system2, gauge_bar, alpha);
}
}
void Window_Base::DrawActorHpValue(const Game_Battler& actor, int cx, int cy) const {
contents->TextDraw(cx, cy, GetValueFontColor(actor.GetHp(), actor.GetMaxHp(), true), std::to_string(actor.GetHp()), Text::AlignRight);
}
void Window_Base::DrawActorSpValue(const Game_Battler& actor, int cx, int cy) const {
contents->TextDraw(cx, cy, GetValueFontColor(actor.GetSp(), actor.GetMaxSp(), false), std::to_string(actor.GetSp()), Text::AlignRight);
}
int Window_Base::GetValueFontColor(int have, int max, bool can_knockout) const {
if (can_knockout && have == 0) return Font::ColorKnockout;
if (max > 0 && (have <= max / 4)) return Font::ColorCritical;
return Font::ColorDefault;
}