1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Headers
19 #include <iomanip>
20 #include <sstream>
21 #include "window_base.h"
22 #include "cache.h"
23 #include <lcf/data.h>
24 #include "game_system.h"
25 #include "bitmap.h"
26 #include "font.h"
27 #include "player.h"
28
Window_Base(int x,int y,int width,int height,Drawable::Flags flags)29 Window_Base::Window_Base(int x, int y, int width, int height, Drawable::Flags flags)
30 : Window(flags)
31 {
32 SetWindowskin(Cache::SystemOrBlack());
33
34 SetX(x);
35 SetY(y);
36 SetWidth(width);
37 SetHeight(height);
38 SetStretch(Main_Data::game_system->GetMessageStretch() == lcf::rpg::System::Stretch_stretch);
39 SetZ(Priority_Window);
40 }
41
InitMovement(int old_x,int old_y,int new_x,int new_y,int duration)42 bool Window_Base::InitMovement(int old_x, int old_y, int new_x, int new_y, int duration) {
43 current_frame = 0;
44 old_position[0] = old_x;
45 old_position[1] = old_y;
46 new_position[0] = new_x;
47 new_position[1] = new_y;
48 SetX(old_position[0]);
49 SetY(old_position[1]);
50 total_frames = 0;
51 if (old_x != new_x || old_y != new_y) {
52 total_frames = duration;
53 return true;
54 }
55 return false;
56 }
57
IsMovementActive()58 bool Window_Base::IsMovementActive() {
59 return total_frames > 0 && current_frame <= total_frames;
60 }
61
Update()62 void Window_Base::Update() {
63 Window::Update();
64 if (IsSystemGraphicUpdateAllowed()) {
65 SetWindowskin(Cache::SystemOrBlack());
66 SetStretch(Main_Data::game_system->GetMessageStretch() == lcf::rpg::System::Stretch_stretch);
67 }
68 UpdateMovement();
69 }
70
UpdateMovement()71 void Window_Base::UpdateMovement() {
72 if (!IsMovementActive()) {
73 return;
74 }
75 current_frame++;
76 if (IsMovementActive()) {
77 SetX(old_position[0] + (new_position[0] - old_position[0]) * current_frame / total_frames);
78 SetY(old_position[1] + (new_position[1] - old_position[1]) * current_frame / total_frames);
79 } else {
80 SetX(new_position[0]);
81 SetY(new_position[1]);
82 }
83 }
84
OnFaceReady(FileRequestResult * result,int face_index,int cx,int cy,bool flip)85 void Window_Base::OnFaceReady(FileRequestResult* result, int face_index, int cx, int cy, bool flip) {
86 BitmapRef faceset = Cache::Faceset(result->file);
87
88 Rect src_rect(
89 (face_index % 4) * 48,
90 face_index / 4 * 48,
91 48,
92 48
93 );
94
95 if (flip) {
96 contents->FlipBlit(cx, cy, *faceset, src_rect, true, false, Opacity::Opaque());
97 }
98 else {
99 contents->Blit(cx, cy, *faceset, src_rect, 255);
100 }
101 }
102
103 // All these functions assume that the input is valid
104
DrawFace(StringView face_name,int face_index,int cx,int cy,bool flip)105 void Window_Base::DrawFace(StringView face_name, int face_index, int cx, int cy, bool flip) {
106 if (face_name.empty()) { return; }
107
108 FileRequestAsync* request = AsyncHandler::RequestFile("FaceSet", face_name);
109 request->SetGraphicFile(true);
110 face_request_ids.push_back(request->Bind(&Window_Base::OnFaceReady, this, face_index, cx, cy, flip));
111 request->Start();
112 }
113
DrawActorFace(const Game_Actor & actor,int cx,int cy)114 void Window_Base::DrawActorFace(const Game_Actor& actor, int cx, int cy) {
115 DrawFace(actor.GetFaceName(), actor.GetFaceIndex(), cx, cy);
116 }
117
DrawActorName(const Game_Battler & actor,int cx,int cy) const118 void Window_Base::DrawActorName(const Game_Battler& actor, int cx, int cy) const {
119 contents->TextDraw(cx, cy, Font::ColorDefault, actor.GetName());
120 }
121
DrawActorTitle(const Game_Actor & actor,int cx,int cy) const122 void Window_Base::DrawActorTitle(const Game_Actor& actor, int cx, int cy) const {
123 contents->TextDraw(cx, cy, Font::ColorDefault, actor.GetTitle());
124 }
125
DrawActorClass(const Game_Actor & actor,int cx,int cy) const126 void Window_Base::DrawActorClass(const Game_Actor& actor, int cx, int cy) const {
127 contents->TextDraw(cx, cy, Font::ColorDefault, actor.GetClassName());
128 }
129
DrawActorLevel(const Game_Actor & actor,int cx,int cy) const130 void Window_Base::DrawActorLevel(const Game_Actor& actor, int cx, int cy) const {
131 // Draw LV-String
132 contents->TextDraw(cx, cy, 1, lcf::Data::terms.lvl_short);
133
134 // Draw Level of the Actor
135 contents->TextDraw(cx + (actor.GetMaxLevel() >= 100 ? 30 : 24), cy, Font::ColorDefault, std::to_string(actor.GetLevel()), Text::AlignRight);
136 }
137
DrawActorState(const Game_Battler & actor,int cx,int cy) const138 void Window_Base::DrawActorState(const Game_Battler& actor, int cx, int cy) const {
139 // Unit has Normal state if no state is set
140 const lcf::rpg::State* state = actor.GetSignificantState();
141 if (!state) {
142 contents->TextDraw(cx, cy, Font::ColorDefault, lcf::Data::terms.normal_status);
143 } else {
144 contents->TextDraw(cx, cy, state->color, state->name);
145 }
146 }
147
DrawActorExp(const Game_Actor & actor,int cx,int cy) const148 void Window_Base::DrawActorExp(const Game_Actor& actor, int cx, int cy) const {
149 // Draw EXP-String
150 int width = 7;
151 if (actor.MaxExpValue() < 1000000) {
152 width = 6;
153 contents->TextDraw(cx, cy, 1, lcf::Data::terms.exp_short);
154 }
155
156 // Current Exp of the Actor
157 // ------/------
158 std::stringstream ss;
159 ss << std::setfill(' ') << std::setw(width) << actor.GetExpString();
160
161 // Delimiter
162 ss << '/';
163
164 // Exp for Level up
165 ss << std::setfill(' ') << std::setw(width) << actor.GetNextExpString();
166 contents->TextDraw(cx + (width == 6 ? 12 : 0), cy, Font::ColorDefault, ss.str(), Text::AlignLeft);
167 }
168
DrawActorHp(const Game_Battler & actor,int cx,int cy,int digits,bool draw_max) const169 void Window_Base::DrawActorHp(const Game_Battler& actor, int cx, int cy, int digits, bool draw_max) const {
170 // Draw HP-String
171 contents->TextDraw(cx, cy, 1, lcf::Data::terms.hp_short);
172
173 // Draw Current HP of the Actor
174 cx += 12;
175 // Color: 0 okay, 4 critical, 5 dead
176 int color = GetValueFontColor(actor.GetHp(), actor.GetMaxHp(), true);
177 auto dx = digits * 6;
178 contents->TextDraw(cx + dx, cy, color, std::to_string(actor.GetHp()), Text::AlignRight);
179
180 if (!draw_max)
181 return;
182
183 // Draw the /
184 cx += dx;
185 contents->TextDraw(cx, cy, Font::ColorDefault, "/");
186
187 // Draw Max Hp
188 cx += 6;
189 contents->TextDraw(cx + dx, cy, Font::ColorDefault, std::to_string(actor.GetMaxHp()), Text::AlignRight);
190 }
191
DrawActorSp(const Game_Battler & actor,int cx,int cy,int digits,bool draw_max) const192 void Window_Base::DrawActorSp(const Game_Battler& actor, int cx, int cy, int digits, bool draw_max) const {
193 // Draw SP-String
194 contents->TextDraw(cx, cy, 1, lcf::Data::terms.sp_short);
195
196 // Draw Current SP of the Actor
197 cx += 12;
198 // Color: 0 okay, 4 critical/empty
199 int color = GetValueFontColor(actor.GetSp(), actor.GetMaxSp(), false);
200 auto dx = digits * 6;
201 contents->TextDraw(cx + dx, cy, color, std::to_string(actor.GetSp()), Text::AlignRight);
202
203 if (!draw_max)
204 return;
205
206 // Draw the /
207 cx += dx;
208 contents->TextDraw(cx, cy, Font::ColorDefault, "/");
209
210 // Draw Max Sp
211 cx += 6;
212 contents->TextDraw(cx + dx, cy, Font::ColorDefault, std::to_string(actor.GetMaxSp()), Text::AlignRight);
213 }
214
DrawActorParameter(const Game_Battler & actor,int cx,int cy,int type) const215 void Window_Base::DrawActorParameter(const Game_Battler& actor, int cx, int cy, int type) const {
216 StringView name;
217 int value;
218
219 switch (type) {
220 case 0:
221 name = lcf::Data::terms.attack;
222 value = actor.GetAtk();
223 break;
224 case 1:
225 name = lcf::Data::terms.defense;
226 value = actor.GetDef();
227 break;
228 case 2:
229 name = lcf::Data::terms.spirit;
230 value = actor.GetSpi();
231 break;
232 case 3:
233 name = lcf::Data::terms.agility;
234 value = actor.GetAgi();
235 break;
236 default:
237 return;
238 }
239
240 // Draw Term
241 contents->TextDraw(cx, cy, 1, name);
242
243 // Draw Value
244 contents->TextDraw(cx + 78, cy, Font::ColorDefault, std::to_string(value), Text::AlignRight);
245 }
246
DrawEquipmentType(const Game_Actor & actor,int cx,int cy,int type) const247 void Window_Base::DrawEquipmentType(const Game_Actor& actor, int cx, int cy, int type) const {
248 StringView name;
249
250 switch (type) {
251 case 0:
252 name = lcf::Data::terms.weapon;
253 break;
254 case 1:
255 if (actor.HasTwoWeapons()) {
256 name = lcf::Data::terms.weapon;
257 } else {
258 name = lcf::Data::terms.shield;
259 }
260 break;
261 case 2:
262 name = lcf::Data::terms.armor;
263 break;
264 case 3:
265 name = lcf::Data::terms.helmet;
266 break;
267 case 4:
268 name = lcf::Data::terms.accessory;
269 break;
270 default:
271 return;
272 }
273
274 contents->TextDraw(cx, cy, 1, name);
275 }
276
DrawItemName(const lcf::rpg::Item & item,int cx,int cy,bool enabled) const277 void Window_Base::DrawItemName(const lcf::rpg::Item& item, int cx, int cy, bool enabled) const {
278 int color = enabled ? Font::ColorDefault : Font::ColorDisabled;
279
280 contents->TextDraw(cx, cy, color, item.name);
281 }
282
DrawSkillName(const lcf::rpg::Skill & skill,int cx,int cy,bool enabled) const283 void Window_Base::DrawSkillName(const lcf::rpg::Skill& skill, int cx, int cy, bool enabled) const {
284 int color = enabled ? Font::ColorDefault : Font::ColorDisabled;
285
286 contents->TextDraw(cx, cy, color, skill.name);
287 }
288
DrawCurrencyValue(int money,int cx,int cy) const289 void Window_Base::DrawCurrencyValue(int money, int cx, int cy) const {
290 // This function draws right aligned because of the dynamic with of the
291 // gold output (cx and cy define the right border)
292 std::stringstream gold;
293 gold << money;
294
295 Rect gold_text_size = Font::Default()->GetSize(lcf::Data::terms.gold);
296 contents->TextDraw(cx, cy, 1, lcf::Data::terms.gold, Text::AlignRight);
297
298 contents->TextDraw(cx - gold_text_size.width, cy, Font::ColorDefault, gold.str(), Text::AlignRight);
299 }
300
DrawGauge(const Game_Battler & actor,int cx,int cy,int alpha) const301 void Window_Base::DrawGauge(const Game_Battler& actor, int cx, int cy, int alpha) const {
302 BitmapRef system2 = Cache::System2();
303 if (!system2) {
304 return;
305 }
306
307 bool full = actor.IsAtbGaugeFull();
308
309 // Which gauge (0 - 2)
310 int gauge_y = 32 + 2 * 16;
311
312 // Three components of the gauge
313 Rect gauge_left(0, gauge_y, 16, 16);
314 Rect gauge_center(16, gauge_y, 16, 16);
315 Rect gauge_right(32, gauge_y, 16, 16);
316
317 Rect dst_rect(cx + 16, cy, 25, 16);
318
319 contents->Blit(cx + 0, cy, *system2, gauge_left, alpha);
320 contents->Blit(cx + 16 + 25, cy, *system2, gauge_right, alpha);
321 contents->StretchBlit(dst_rect, *system2, gauge_center, alpha);
322
323 const auto atb = actor.GetAtbGauge();
324 const auto gauge_w = 25 * atb / actor.GetMaxAtbGauge();
325 if (gauge_w > 0) {
326 // Full or not full bar
327 Rect gauge_bar(full ? 64 : 48, gauge_y, 16, 16);
328 Rect bar_rect(cx + 16, cy, gauge_w, 16);
329 contents->StretchBlit(bar_rect, *system2, gauge_bar, alpha);
330 }
331 }
332
DrawActorHpValue(const Game_Battler & actor,int cx,int cy) const333 void Window_Base::DrawActorHpValue(const Game_Battler& actor, int cx, int cy) const {
334 contents->TextDraw(cx, cy, GetValueFontColor(actor.GetHp(), actor.GetMaxHp(), true), std::to_string(actor.GetHp()), Text::AlignRight);
335 }
336
DrawActorSpValue(const Game_Battler & actor,int cx,int cy) const337 void Window_Base::DrawActorSpValue(const Game_Battler& actor, int cx, int cy) const {
338 contents->TextDraw(cx, cy, GetValueFontColor(actor.GetSp(), actor.GetMaxSp(), false), std::to_string(actor.GetSp()), Text::AlignRight);
339 }
340
GetValueFontColor(int have,int max,bool can_knockout) const341 int Window_Base::GetValueFontColor(int have, int max, bool can_knockout) const {
342 if (can_knockout && have == 0) return Font::ColorKnockout;
343 if (max > 0 && (have <= max / 4)) return Font::ColorCritical;
344 return Font::ColorDefault;
345 }
346