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