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 #define _USE_MATH_DEFINES
20 #include <cmath>
21 #include "system.h"
22 #include "player.h"
23 #include "rect.h"
24 #include "util_macro.h"
25 #include "window.h"
26 #include "bitmap.h"
27 #include "drawable_mgr.h"
28 
29 constexpr int pause_animation_frames = 20;
30 
Window(Drawable::Flags flags)31 Window::Window(Drawable::Flags flags): Drawable(Priority_Window, flags)
32 {
33 	DrawableMgr::Register(this);
34 }
35 
SetOpenAnimation(int frames)36 void Window::SetOpenAnimation(int frames) {
37 	closing = false;
38 	SetVisible(true);
39 
40 	if (frames > 0) {
41 		animation_frames = frames;
42 		animation_count = 0.0;
43 		animation_increment = (height / 2.0) / frames;
44 	}
45 	else {
46 		animation_frames = 0;
47 	}
48 }
49 
SetCloseAnimation(int frames)50 void Window::SetCloseAnimation(int frames) {
51 	if (frames > 0) {
52 		closing = true;
53 		animation_frames = frames;
54 		animation_count = (height / 2.0);
55 		animation_increment = - animation_count / frames;
56 	} else {
57 		SetVisible(false);
58 	}
59 }
60 
Draw(Bitmap & dst)61 void Window::Draw(Bitmap& dst) {
62 	if (!IsVisible()) return;
63 	if (width <= 0 || height <= 0) return;
64 	if (x < -width || x > dst.GetWidth() || y < -height || y > dst.GetHeight()) return;
65 
66 	if (windowskin) {
67 		if (width > 4 && height > 4 && (back_opacity * opacity / 255 > 0)) {
68 			if (background_needs_refresh) RefreshBackground();
69 
70 			if (animation_frames > 0) {
71 				int ianimation_count = (int)animation_count;
72 
73 				Rect src_rect(0, height / 2 - ianimation_count, width, ianimation_count * 2);
74 
75 				dst.Blit(x, y + src_rect.y, *background, src_rect, back_opacity * opacity / 255);
76 			} else {
77 				dst.Blit(x, y, *background, background->GetRect(), back_opacity * opacity / 255);
78 			}
79 		}
80 
81 		if (width > 0 && height > 0 && opacity > 0) {
82 			if (frame_needs_refresh) RefreshFrame();
83 
84 			if (animation_frames > 0) {
85 				int ianimation_count = (int)animation_count;
86 
87 				if (ianimation_count > 8) {
88 					Rect src_rect(0, height / 2 - ianimation_count, 8, ianimation_count * 2 - 16);
89 
90 					dst.Blit(x, y + 8 + src_rect.y, *frame_left, src_rect, opacity);
91 					dst.Blit(x + width - 8, y + 8 + src_rect.y, *frame_right, src_rect, opacity);
92 
93 					dst.Blit(x, y + height / 2 - ianimation_count, *frame_up, frame_up->GetRect(), opacity);
94 					dst.Blit(x, y + height / 2 + ianimation_count - 8, *frame_down, frame_down->GetRect(), opacity);
95 				} else {
96 					dst.Blit(x, y + height / 2 - ianimation_count, *frame_up, Rect(0, 0, width, ianimation_count), opacity);
97 					dst.Blit(x, y + height / 2 , *frame_down, Rect(0, 8 - ianimation_count, width, ianimation_count), opacity);
98 				}
99 			} else {
100 				dst.Blit(x, y, *frame_up, frame_up->GetRect(), opacity);
101 				dst.Blit(x, y + height - 8, *frame_down, frame_down->GetRect(), opacity);
102 				dst.Blit(x, y + 8, *frame_left, frame_left->GetRect(), opacity);
103 				dst.Blit(x + width - 8, y + 8, *frame_right, frame_right->GetRect(), opacity);
104 			}
105 		}
106 
107 		if (width >= 16 && height > 16 && cursor_rect.width > 4 && cursor_rect.height > 4 && animation_frames == 0) {
108 			if (cursor_needs_refresh) RefreshCursor();
109 
110 			Rect src_rect(
111 				-min(cursor_rect.x + border_x, 0),
112 				-min(cursor_rect.y + border_y, 0),
113 				min(cursor_rect.width, width - cursor_rect.x + border_x),
114 				min(cursor_rect.height, height - cursor_rect.y + border_y)
115 			);
116 
117 			if (cursor_frame <= 10)
118 				dst.Blit(x + cursor_rect.x + border_x, y + cursor_rect.y + border_y, *cursor1, src_rect, 255);
119 			else
120 				dst.Blit(x + cursor_rect.x + border_x, y + cursor_rect.y + border_y, *cursor2, src_rect, 255);
121 		}
122 	}
123 
124 	if (contents) {
125 		if (width > 2 * border_x && height > 2 * border_y &&
126 			-ox < width - 2 * border_x && -oy < height - 2 * border_y &&
127 			contents_opacity > 0 && animation_frames == 0) {
128 			Rect src_rect(-min(-ox, 0), -min(-oy, 0),
129 						  min(width - 2 * border_x, width - 2 * border_x + ox),
130 						  min(height - 2 * border_y, height - 2 * border_y + oy));
131 
132 			dst.Blit(max(x + border_x, x + border_x - ox),
133 					  max(y + border_y, y + border_y - oy),
134 					  *contents, src_rect, contents_opacity);
135 		}
136 	}
137 
138 	if ((pause && pause_frame < pause_animation_frames && animation_frames <= 0) || down_arrow) {
139 		Rect src_rect(40, 16, 16, 8);
140 		dst.Blit(x + width / 2 - 8, y + height - 8, *windowskin, src_rect, 255);
141 	}
142 
143 	if (up_arrow) {
144 		Rect src_rect(40, 8, 16, 8);
145 		dst.Blit(x + width / 2 - 8, y, *windowskin, src_rect, 255);
146 	}
147 
148 	if (right_arrow) {
149 		Rect src_rect(40, 16, 16, 8);
150 		dst.RotateZoomOpacityBlit(x + width - 8, y + height / 2 - 8, 16, 0, *windowskin, src_rect, -M_PI / 2, 1.0, 1.0, 255);
151 	}
152 
153 	if (left_arrow) {
154 		Rect src_rect(40, 8, 16, 8);
155 		dst.RotateZoomOpacityBlit(x, y + height / 2 - 8, 16, 0, *windowskin, src_rect, -M_PI / 2, 1.0, 1.0, 255);
156 	}
157 }
158 
RefreshBackground()159 void Window::RefreshBackground() {
160 	background_needs_refresh = false;
161 
162 	BitmapRef bitmap = Bitmap::Create(width, height, false);
163 
164 	if (stretch) {
165 		bitmap->StretchBlit(*windowskin, Rect(0, 0, 32, 32), 255);
166 	} else {
167 		bitmap->TiledBlit(Rect(0, 0, 32, 32), *windowskin, bitmap->GetRect(), 255);
168 	}
169 
170 	background = bitmap;
171 }
172 
RefreshFrame()173 void Window::RefreshFrame() {
174 	frame_needs_refresh = false;
175 
176 	BitmapRef up_bitmap = Bitmap::Create(width, 8);
177 	BitmapRef down_bitmap = Bitmap::Create(width, 8);
178 
179 	up_bitmap->Clear();
180 	down_bitmap->Clear();
181 
182 	Rect src_rect, dst_rect;
183 
184 	// Border Up
185 	src_rect = { 32 + 8, 0, 16, 8 };
186 	dst_rect = { 8, 0, max(width - 16, 1), 8 };
187 	up_bitmap->TiledBlit(8, 0, src_rect, *windowskin, dst_rect, 255);
188 
189 	// Border Down
190 	src_rect = { 32 + 8, 32 - 8, 16, 8 };
191 	dst_rect = { 8, 0, max(width - 16, 1), 8 };
192 	down_bitmap->TiledBlit(8, 0, src_rect, *windowskin, dst_rect, 255);
193 
194 	// Upper left corner
195 	up_bitmap->Blit(0, 0, *windowskin, Rect(32, 0, 8, 8), 255);
196 
197 	// Upper right corner
198 	up_bitmap->Blit(width - 8, 0, *windowskin, Rect(64 - 8, 0, 8, 8), 255);
199 
200 	// Lower left corner
201 	down_bitmap->Blit(0, 0, *windowskin, Rect(32, 32 - 8, 8, 8), 255);
202 
203 	// Lower right corner
204 	down_bitmap->Blit(width - 8, 0, *windowskin, Rect(64 - 8, 32 - 8, 8, 8), 255);
205 
206 	frame_up = up_bitmap;
207 	frame_down = down_bitmap;
208 
209 	if (height > 16) {
210 		BitmapRef left_bitmap = Bitmap::Create(8, height - 16);
211 		BitmapRef right_bitmap = Bitmap::Create(8, height - 16);
212 
213 		left_bitmap->Clear();
214 		right_bitmap->Clear();
215 
216 		// Border Left
217 		src_rect = { 32, 8, 8, 16 };
218 		dst_rect = { 0, 0, 8, height - 16 };
219 		left_bitmap->TiledBlit(0, 8, src_rect, *windowskin, dst_rect, 255);
220 
221 		// Border Right
222 		src_rect = { 64 - 8, 8, 8, 16 };
223 		dst_rect = { 0, 0, 8, height - 16 };
224 		right_bitmap->TiledBlit(0, 8, src_rect, *windowskin, dst_rect, 255);
225 
226 		frame_left = left_bitmap;
227 		frame_right = right_bitmap;
228 	} else {
229 		frame_left = BitmapRef();
230 		frame_right = BitmapRef();
231 	}
232 }
233 
RefreshCursor()234 void Window::RefreshCursor() {
235 	cursor_needs_refresh = false;
236 
237 	int cw = cursor_rect.width;
238 	int ch = cursor_rect.height;
239 
240 	BitmapRef cursor1_bitmap = Bitmap::Create(cw, ch);
241 	BitmapRef cursor2_bitmap = Bitmap::Create(cw, ch);
242 
243 	cursor1_bitmap->Clear();
244 	cursor2_bitmap->Clear();
245 
246 	Rect dst_rect;
247 
248 	// Border Up
249 	dst_rect = { 8, 0, cw - 16, 8 };
250 	cursor1_bitmap->TiledBlit(8, 0, Rect(64 + 8, 0, 16, 8), *windowskin, dst_rect, 255);
251 	cursor2_bitmap->TiledBlit(8, 0, Rect(96 + 8, 0, 16, 8), *windowskin, dst_rect, 255);
252 
253 	// Border Down
254 	dst_rect = { 8, ch - 8, cw - 16, 8 };
255 	cursor1_bitmap->TiledBlit(8, 0, Rect(64 + 8, 32 - 8, 16, 8), *windowskin, dst_rect, 255);
256 	cursor2_bitmap->TiledBlit(8, 0, Rect(96 + 8, 32 - 8, 16, 8), *windowskin, dst_rect, 255);
257 
258 	// Border Left
259 	dst_rect = { 0, 8, 8, ch - 16 };
260 	cursor1_bitmap->TiledBlit(0, 8, Rect(64, 8, 8, 16), *windowskin, dst_rect, 255);
261 	cursor2_bitmap->TiledBlit(0, 8, Rect(96, 8, 8, 16), *windowskin, dst_rect, 255);
262 
263 	// Border Right
264 	dst_rect = { cw - 8, 8, 8, ch - 16 };
265 	cursor1_bitmap->TiledBlit(0, 8, Rect(96 - 8, 8, 8, 16), *windowskin, dst_rect, 255);
266 	cursor2_bitmap->TiledBlit(0, 8, Rect(128 - 8, 8, 8, 16), *windowskin, dst_rect, 255);
267 
268 	// Upper left corner
269 	cursor1_bitmap->Blit(0, 0, *windowskin, Rect(64, 0, 8, 8), 255);
270 	cursor2_bitmap->Blit(0, 0, *windowskin, Rect(96, 0, 8, 8), 255);
271 
272 	// Upper right corner
273 	cursor1_bitmap->Blit(cw - 8, 0, *windowskin, Rect(96 - 8, 0, 8, 8), 255);
274 	cursor2_bitmap->Blit(cw - 8, 0, *windowskin, Rect(128 - 8, 0, 8, 8), 255);
275 
276 	// Lower left corner
277 	cursor1_bitmap->Blit(0, ch - 8, *windowskin, Rect(64, 32 - 8, 8, 8), 255);
278 	cursor2_bitmap->Blit(0, ch - 8, *windowskin, Rect(96, 32 - 8, 8, 8), 255);
279 
280 	// Lower right corner
281 	cursor1_bitmap->Blit(cw - 8, ch - 8, *windowskin, Rect(96 - 8, 32 - 8, 8, 8), 255);
282 	cursor2_bitmap->Blit(cw - 8, ch - 8, *windowskin, Rect(128 - 8, 32 - 8, 8, 8), 255);
283 
284 	// Background
285 	dst_rect = { 8, 8, cw - 16, ch - 16 };
286 	cursor1_bitmap->TiledBlit(8, 8, Rect(64 + 8, 8, 16, 16), *windowskin, dst_rect, 255);
287 	cursor2_bitmap->TiledBlit(8, 8, Rect(96 + 8, 8, 16, 16), *windowskin, dst_rect, 255);
288 
289 	cursor1 = cursor1_bitmap;
290 	cursor2 = cursor2_bitmap;
291 }
292 
Update()293 void Window::Update() {
294 	if (active) {
295 		cursor_frame += 1;
296 		if (cursor_frame > 20) cursor_frame = 0;
297 		if (pause) {
298 			pause_frame = (pause_frame + 1) % (pause_animation_frames * 2);
299 		}
300 	}
301 
302 	if (animation_frames > 0) {
303 		// Open/Close Animation
304 		animation_frames -= 1;
305 		animation_count += animation_increment;
306 		if (closing && animation_frames <= 0) {
307 			SetVisible(false);
308 			closing = false;
309 		}
310 	}
311 }
312 
SetWindowskin(BitmapRef const & nwindowskin)313 void Window::SetWindowskin(BitmapRef const& nwindowskin) {
314 	if (windowskin == nwindowskin) {
315 		return;
316 	}
317 	background_needs_refresh = true;
318 	frame_needs_refresh = true;
319 	cursor_needs_refresh = true;
320 	windowskin = nwindowskin;
321 }
322 
SetStretch(bool nstretch)323 void Window::SetStretch(bool nstretch) {
324 	if (stretch != nstretch) background_needs_refresh = true;
325 	stretch = nstretch;
326 }
327 
SetCursorRect(Rect const & ncursor_rect)328 void Window::SetCursorRect(Rect const& ncursor_rect) {
329 	if (cursor_rect.width != ncursor_rect.width || cursor_rect.height != ncursor_rect.height) cursor_needs_refresh = true;
330 	cursor_rect = ncursor_rect;
331 }
332 
SetWidth(int nwidth)333 void Window::SetWidth(int nwidth) {
334 	if (width != nwidth) {
335 		background_needs_refresh = true;
336 		frame_needs_refresh = true;
337 	}
338 	width = nwidth;
339 }
340 
SetHeight(int nheight)341 void Window::SetHeight(int nheight) {
342 	if (height != nheight) {
343 		background_needs_refresh = true;
344 		frame_needs_refresh = true;
345 	}
346 	height = nheight;
347 }
348 
349 
350