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 #ifndef EP_GAME_SYSTEM_H
19 #define EP_GAME_SYSTEM_H
20 
21 // Headers
22 #include <string>
23 #include <map>
24 #include <lcf/rpg/animation.h>
25 #include <lcf/rpg/music.h>
26 #include <lcf/rpg/sound.h>
27 #include <lcf/rpg/system.h>
28 #include <lcf/rpg/savesystem.h>
29 #include "color.h"
30 #include "transition.h"
31 #include "string_view.h"
32 #include "async_handler.h"
33 #include "filesystem_stream.h"
34 
35 struct FileRequestResult;
36 
37 /**
38  * Game System namespace.
39  */
40 class Game_System {
41 public:
42 	enum sys_bgm {
43 		BGM_Battle,
44 		BGM_Victory,
45 		BGM_Inn,
46 		BGM_Boat,
47 		BGM_Ship,
48 		BGM_Airship,
49 		BGM_GameOver,
50 
51 		BGM_Count
52 	};
53 
54 	enum sys_sfx {
55 		SFX_Cursor,
56 		SFX_Decision,
57 		SFX_Cancel,
58 		SFX_Buzzer,
59 		SFX_BeginBattle,
60 		SFX_Escape,
61 		SFX_EnemyAttacks,
62 		SFX_EnemyDamage,
63 		SFX_AllyDamage,
64 		SFX_Evasion,
65 		SFX_EnemyKill,
66 		SFX_UseItem,
67 
68 		SFX_Count
69 	};
70 
71 	enum sys_transition {
72 		Transition_TeleportErase,
73 		Transition_TeleportShow,
74 		Transition_BeginBattleErase,
75 		Transition_BeginBattleShow,
76 		Transition_EndBattleErase,
77 		Transition_EndBattleShow,
78 		Transition_Count
79 	};
80 
81 	using AtbMode = lcf::rpg::SaveSystem::AtbMode;
82 
83 	class Target {
84 	public:
85 		int map_id;
86 		int x;
87 		int y;
88 		int switch_id;
Target()89 		Target()
90 			: map_id(0),
91 			  x(0),
92 			  y(0),
93 			  switch_id(0)
94 		{}
Target(int map_id,int x,int y,int switch_id)95 		Target(int map_id, int x, int y, int switch_id)
96 			: map_id(map_id),
97 			  x(x),
98 			  y(y),
99 			  switch_id(switch_id)
100 		{}
101 	};
102 
103 	/**
104 	 * Initializes Game System.
105 	 */
106 	Game_System();
107 
108 	/** Initialize from save game */
109 	void SetupFromSave(lcf::rpg::SaveSystem save);
110 
111 	/** @return save game data */
112 	const lcf::rpg::SaveSystem& GetSaveData() const;
113 
114 	/**
115 	 * Plays a Music.
116 	 *
117 	 * @param bgm music data.
118 	 */
119 	void BgmPlay(lcf::rpg::Music const& bgm);
120 
121 	/**
122 	 * Stops playing music.
123 	 */
124 	void BgmStop();
125 
126 	/**
127 	 * Fades out the current BGM
128 	 *
129 	 * @param duration Duration in ms
130 	 * @param clear_current_music If true then current_music is set to (OFF). Only needed on starting a new game.
131 	 */
132 	void BgmFade(int duration, bool clear_current_music = false);
133 
134 	/**
135 	 * Plays a Sound.
136 	 *
137 	 * @param se sound data.
138 	 * @param stop_sounds If true stops all SEs when playing (OFF)/(...). Only used by the interpreter.
139 	 */
140 	void SePlay(const lcf::rpg::Sound& se, bool stop_sounds = false);
141 
142 	/**
143 	 * Plays the first valid sound in the animation.
144 	 *
145 	 * @param animation animation data.
146 	 */
147 	void SePlay(const lcf::rpg::Animation& animation);
148 
149 	/** @return system graphic filename.  */
150 	StringView GetSystemName();
151 
152 	/** @return message stretch style */
153 	lcf::rpg::System::Stretch GetMessageStretch();
154 
155 	/** @return system font */
156 	lcf::rpg::System::Font GetFontId();
157 
158 	/**
159 	 * Sets the system graphic.
160 	 *
161 	 * @param system_name new system name.
162 	 * @param message_stretch message stretch style
163 	 * @param font_id The system font to use.
164 	 */
165 	void SetSystemGraphic(const std::string& system_name,
166 			lcf::rpg::System::Stretch stretch,
167 			lcf::rpg::System::Font font);
168 
169 	/** Resets the system graphic to the default value. */
170 	void ResetSystemGraphic();
171 
172 	/** @return the system2 graphic name */
173 	StringView GetSystem2Name();
174 
175 	/** @return true if the game has a configured system graphic */
176 	bool HasSystemGraphic();
177 
178 	/** @return true if the game has a configured system2 graphic */
179 	bool HasSystem2Graphic();
180 
181 	/**
182 	 * Gets the system music.
183 	 *
184 	 * @param which which "context" to set the music for.
185 	 * @return the music.
186 	 */
187 	const lcf::rpg::Music& GetSystemBGM(int which);
188 
189 	/**
190 	 * Sets the system music.
191 	 *
192 	 * @param which which "context" to set the music for.
193 	 * @param bgm the music.
194 	 */
195 	void SetSystemBGM(int which, lcf::rpg::Music bgm);
196 
197 	/**
198 	 * Gets the system sound effects.
199 	 *
200 	 * @param which which "context" to set the music for.
201 	 * @return the sound.
202 	 */
203 	const lcf::rpg::Sound& GetSystemSE(int which);
204 
205 	/**
206 	 * Sets a system sound effect.
207 	 *
208 	 * @param which which "context" to set the effect for.
209 	 * @param sfx the sound effect.
210 	 */
211 	void SetSystemSE(int which, lcf::rpg::Sound sfx);
212 
213 	/**
214 	 * Gets the system transitions.
215 	 *
216 	 * @param which which "context" to get the transition for.
217 	 * @return the transition.
218 	 */
219 	Transition::Type GetTransition(int which);
220 
221 	/**
222 	 * Sets the system transitions.
223 	 *
224 	 * @param which which "context" to set the transition for.
225 	 * @param transition the transition.
226 	 */
227 	void SetTransition(int which, int transition);
228 
229 	/**
230 	 * Sets a teleport target.
231 	 *
232 	 * @param map_id the destination map.
233 	 * @param x the destination X coordinate.
234 	 * @param y the destination Y coordinate.
235 	 * @param switch_id the switch ID.
236 	 */
237 	void AddTeleportTarget(int map_id, int x, int y, int switch_id);
238 
239 	/**
240 	 * Removes a teleport target.
241 	 *
242 	 * @param map_id the map for which the target was used.
243 	 */
244 	void RemoveTeleportTarget(int map_id);
245 
246 	/**
247 	 * Finds a teleport target.
248 	 *
249 	 * @param map_id the map for which to obtain the target.
250 	 * @return: pointer to a Target structure, or NULL.
251 	 */
252 	Target* GetTeleportTarget(int map_id);
253 
254 	/**
255 	 * Sets an escape  target.
256 	 *
257 	 * @param map_id the destination map.
258 	 * @param x the destination X coordinate.
259 	 * @param y the destination Y coordinate.
260 	 * @param switch_id the switch ID.
261 	 */
262 	void SetEscapeTarget(int map_id, int x, int y, int switch_id);
263 
264 	/**
265 	 * Finds an escape target.
266 	 *
267 	 * @return pointer to a Target structure, or NULL.
268 	 */
269 	Target* GetEscapeTarget();
270 
271 	bool GetAllowTeleport();
272 	void SetAllowTeleport(bool allow);
273 	bool GetAllowEscape();
274 	void SetAllowEscape(bool allow);
275 	bool GetAllowSave();
276 	void SetAllowSave(bool allow);
277 	bool GetAllowMenu();
278 	void SetAllowMenu(bool allow);
279 
280 	int GetSaveCount();
281 	void IncSaveCount();
282 
283 	const lcf::rpg::Music& GetCurrentBGM();
284 	void MemorizeBGM();
285 	void PlayMemorizedBGM();
286 
287 	void ReloadSystemGraphic();
288 
289 	/**
290 	 * Determines if the requested file is supposed to Stop BGM/SE play.
291 	 * For empty string and (OFF) this is always the case.
292 	 * Many RPG Maker translation overtranslated the (OFF) reserved string,
293 	 * e.g. (Brak) and (Kein Sound).
294 	 * A file is detected as "Stop BGM/SE" when the file is missing in the
295 	 * filesystem and the name is wrapped in (), otherwise it is a regular
296 	 * file.
297 	 *
298 	 * @param name File to find
299 	 * @param find_func Find function to use (OpenSound or OpenMusic)
300 	 * @param found_stream Handle to the file to play
301 	 * @return true when the file is supposed to Stop playback.
302 	 *         false otherwise and a handle to the file is returned in found_stream
303 	 */
304 	static bool IsStopFilename(StringView name, Filesystem_Stream::InputStream (*find_func) (StringView), Filesystem_Stream::InputStream& found_stream);
305 
306 	static bool IsStopMusicFilename(StringView name, Filesystem_Stream::InputStream& found_stream);
307 	static bool IsStopMusicFilename(StringView name);
308 	static bool IsStopSoundFilename(StringView name, Filesystem_Stream::InputStream& found_stream);
309 	static bool IsStopSoundFilename(StringView name);
310 
311 	/** @return current atb mode */
312 	AtbMode GetAtbMode();
313 
314 	/** Set the atb mode */
315 	void SetAtbMode(AtbMode m);
316 
317 	/** Flip the atb mode to the opposite */
318 	void ToggleAtbMode();
319 
320 	/** @return Music playing before battle started */
321 	const lcf::rpg::Music& GetBeforeBattleMusic();
322 
323 	/**
324 	 * Set before battle music
325 	 *
326 	 * @param music music to set
327 	 */
328 	void SetBeforeBattleMusic(lcf::rpg::Music music);
329 
330 	/** @return Music playing before boarded vehicle */
331 	const lcf::rpg::Music& GetBeforeVehicleMusic();
332 
333 	/**
334 	 * Set before vehicle music
335 	 *
336 	 * @param name name of music to set
337 	 */
338 	void SetBeforeVehicleMusic(lcf::rpg::Music music);
339 
340 	/** @return save slot used for last save game */
341 	int GetSaveSlot();
342 
343 	/**
344 	 * Set the save slot used when saving the game
345 	 *
346 	 * @param slot the slot number
347 	 */
348 	void SetSaveSlot(int slot);
349 
350 	/** @return RPG_RT compatible frame counter */
351 	int GetFrameCounter();
352 
353 	/** Reset the RPG_RT compatible frame counter to 0 */
354 	void ResetFrameCounter();
355 
356 	/** Increment the RPG_RT compatible frame counter */
357 	void IncFrameCounter();
358 
359 	/** Get the system background color */
360 	Color GetBackgroundColor();
361 
362 	/** @return true if battle animations are flipped if attacked from behind */
363 	bool GetInvertAnimations();
364 
365 	/** Reset the face graphic. */
366 	void ClearMessageFace();
367 
368 	/** @return name of file that contains the face. */
369 	StringView GetMessageFaceName();
370 
371 	/**
372 	 * Set FaceSet graphic file containing the face.
373 	 *
374 	 * @param face FaceSet file
375 	 */
376 	void SetMessageFaceName(const std::string& face);
377 
378 	/**
379 	 * Gets index of the face to display.
380 	 *
381 	 * @return face index
382 	 */
383 	int GetMessageFaceIndex();
384 
385 	/**
386 	 * Sets index of the face to display
387 	 *
388 	 * @param index face index
389 	 */
390 	void SetMessageFaceIndex(int index);
391 
392 	/**
393 	 * Whether to mirror the face.
394 	 *
395 	 * @return true: flipped, false: normal
396 	 */
397 	bool IsMessageFaceFlipped();
398 
399 	/**
400 	 * Sets whether to mirror the face.
401 	 *
402 	 * @param flipped Enable/Disable mirroring
403 	 */
404 	void SetMessageFaceFlipped(bool flipped);
405 
406 	/**
407 	 * If the face shall be placed right.
408 	 *
409 	 * @return true: right side, false: left side
410 	 */
411 	bool IsMessageFaceRightPosition();
412 
413 	/**
414 	 * Sets the face position.
415 	 *
416 	 * @param right true: right side, false: left side
417 	 */
418 	void SetMessageFaceRightPosition(bool right);
419 
420 	/**
421 	 * Gets if the message background is transparent.
422 	 *
423 	 * @return message transparent
424 	 */
425 	bool IsMessageTransparent();
426 
427 	/**
428 	 * Sets message box background state
429 	 *
430 	 * @param transparent true: transparent, false: opaque
431 	 */
432 	void SetMessageTransparent(bool transparent);
433 
434 	/**
435 	 * Gets the message box position.
436 	 *
437 	 * @return 0: top, 1: middle, 2: bottom
438 	 */
439 	int GetMessagePosition();
440 
441 	/**
442 	 * Sets the message box position.
443 	 * Depending on the player position this value is ignored to prevent overlap.
444 	 * (see SetPositionFixed)
445 	 *
446 	 * @param new_position 0: top, 1: middle, 2: bottom
447 	 */
448 	void SetMessagePosition(int new_position);
449 
450 	/**
451 	 * Gets whether message box position is fixed.
452 	 * In that case the hero can be obstructed.
453 	 *
454 	 * @return fixed
455 	 */
456 	bool IsMessagePositionFixed();
457 
458 	/**
459 	 * Sets if message box is moved to avoid obscuring the player.
460 	 *
461 	 * @param fixed position fixed
462 	 */
463 	void SetMessagePositionFixed(bool fixed);
464 
465 	/**
466 	 * Gets if parallel events continue while message box is displayed.
467 	 *
468 	 * @return whether events continue
469 	 */
470 	bool GetMessageContinueEvents();
471 
472 	/**
473 	 * Sets if parallel events continue while message box is displayed.
474 	 *
475 	 * @param continue_events continue events
476 	 */
477 	void SetMessageContinueEvents(bool continue_events);
478 
479 	/**
480 	 * Sets the RpgRt event message active flag.
481 	 *
482 	 * @param value what to set the flag to
483 	 */
484 	void SetMessageEventMessageActive(bool value);
485 
486 	/** @return the RpgRt event message active flag */
487 	bool GetMessageEventMessageActive();
488 private:
489 	void OnBgmReady(FileRequestResult* result);
490 	void OnBgmInelukiReady(FileRequestResult* result);
491 	void OnSeReady(FileRequestResult* result, lcf::rpg::Sound se, bool stop_sounds);
492 	void OnChangeSystemGraphicReady(FileRequestResult* result);
493 private:
494 	lcf::rpg::SaveSystem data;
495 	const lcf::rpg::System* dbsys;
496 	FileRequestBinding music_request_id;
497 	FileRequestBinding system_request_id;
498 	std::map<std::string, FileRequestBinding> se_request_ids;
499 	Color bg_color = Color{ 0, 0, 0, 255 };
500 	bool bgm_pending = false;
501 };
502 
HasSystemGraphic()503 inline bool Game_System::HasSystemGraphic() {
504 	return !GetSystemName().empty();
505 }
506 
HasSystem2Graphic()507 inline bool Game_System::HasSystem2Graphic() {
508 	return !GetSystem2Name().empty();
509 }
510 
IsStopMusicFilename(StringView name)511 inline bool Game_System::IsStopMusicFilename(StringView name) {
512 	Filesystem_Stream::InputStream s;
513 	return IsStopMusicFilename(name, s);
514 }
515 
IsStopSoundFilename(StringView name)516 inline bool Game_System::IsStopSoundFilename(StringView name) {
517 	Filesystem_Stream::InputStream s;
518 	return IsStopSoundFilename(name, s);
519 }
520 
ClearMessageFace()521 inline void Game_System::ClearMessageFace() {
522 	SetMessageFaceName("");
523 	SetMessageFaceIndex(0);
524 }
525 
GetMessageFaceName()526 inline StringView Game_System::GetMessageFaceName() {
527 	return data.face_name;
528 }
529 
SetMessageFaceName(const std::string & face)530 inline void Game_System::SetMessageFaceName(const std::string& face) {
531 	data.face_name = face;
532 }
533 
GetMessageFaceIndex()534 inline int Game_System::GetMessageFaceIndex() {
535 	return data.face_id;
536 }
537 
SetMessageFaceIndex(int index)538 inline void Game_System::SetMessageFaceIndex(int index) {
539 	data.face_id = index;
540 }
541 
IsMessageFaceFlipped()542 inline bool Game_System::IsMessageFaceFlipped() {
543 	return data.face_flip;
544 }
545 
SetMessageFaceFlipped(bool flipped)546 inline void Game_System::SetMessageFaceFlipped(bool flipped) {
547 	data.face_flip = flipped;
548 }
549 
IsMessageFaceRightPosition()550 inline bool Game_System::IsMessageFaceRightPosition() {
551 	return data.face_right;
552 }
553 
SetMessageFaceRightPosition(bool right)554 inline void Game_System::SetMessageFaceRightPosition(bool right) {
555 	data.face_right = right;
556 }
557 
SetMessageTransparent(bool transparent)558 inline void Game_System::SetMessageTransparent(bool transparent) {
559 	data.message_transparent = transparent;
560 }
561 
GetMessagePosition()562 inline int Game_System::GetMessagePosition() {
563 	return data.message_position;
564 }
565 
SetMessagePosition(int new_position)566 inline void Game_System::SetMessagePosition(int new_position) {
567 	data.message_position = new_position;
568 }
569 
IsMessagePositionFixed()570 inline bool Game_System::IsMessagePositionFixed() {
571 	return !data.message_prevent_overlap;
572 }
573 
SetMessagePositionFixed(bool fixed)574 inline void Game_System::SetMessagePositionFixed(bool fixed) {
575 	data.message_prevent_overlap = !fixed;
576 }
577 
GetMessageContinueEvents()578 inline bool Game_System::GetMessageContinueEvents() {
579 	return data.message_continue_events;
580 }
581 
SetMessageContinueEvents(bool continue_events)582 inline void Game_System::SetMessageContinueEvents(bool continue_events) {
583 	data.message_continue_events = continue_events;
584 }
585 
SetMessageEventMessageActive(bool value)586 inline void Game_System::SetMessageEventMessageActive(bool value) {
587 	data.event_message_active = value;
588 }
589 
GetMessageEventMessageActive()590 inline bool Game_System::GetMessageEventMessageActive() {
591 	return data.event_message_active;
592 }
593 
GetAtbMode()594 inline Game_System::AtbMode Game_System::GetAtbMode() {
595 	return static_cast<Game_System::AtbMode>(data.atb_mode);
596 }
597 
SetAtbMode(AtbMode m)598 inline void Game_System::SetAtbMode(AtbMode m) {
599 	data.atb_mode = m;
600 }
601 
ToggleAtbMode()602 inline void Game_System::ToggleAtbMode() {
603 	data.atb_mode = !data.atb_mode;
604 }
605 
GetBeforeBattleMusic()606 inline const lcf::rpg::Music& Game_System::GetBeforeBattleMusic() {
607 	return data.before_battle_music;
608 }
609 
SetBeforeBattleMusic(lcf::rpg::Music music)610 inline void Game_System::SetBeforeBattleMusic(lcf::rpg::Music music) {
611 	data.before_battle_music = std::move(music);
612 }
613 
GetBeforeVehicleMusic()614 inline const lcf::rpg::Music& Game_System::GetBeforeVehicleMusic() {
615 	return data.before_vehicle_music;
616 }
617 
SetBeforeVehicleMusic(lcf::rpg::Music music)618 inline void Game_System::SetBeforeVehicleMusic(lcf::rpg::Music music) {
619 	data.before_vehicle_music = std::move(music);
620 }
621 
GetSaveSlot()622 inline int Game_System::GetSaveSlot() {
623 	return data.save_slot;
624 }
625 
SetSaveSlot(int slot)626 inline void Game_System::SetSaveSlot(int slot) {
627 	data.save_slot = slot;
628 }
629 
GetFrameCounter()630 inline int Game_System::GetFrameCounter() {
631 	return data.frame_count;
632 }
633 
ResetFrameCounter()634 inline void Game_System::ResetFrameCounter() {
635 	data.frame_count = 0;
636 }
637 
IncFrameCounter()638 inline void Game_System::IncFrameCounter() {
639 	++data.frame_count;
640 }
641 
GetBackgroundColor()642 inline Color Game_System::GetBackgroundColor() {
643 	return bg_color;
644 }
645 
GetInvertAnimations()646 inline bool Game_System::GetInvertAnimations() {
647 	return dbsys->invert_animations;
648 }
649 
GetSaveCount()650 inline int Game_System::GetSaveCount() {
651 	return data.save_count;
652 }
653 
IncSaveCount()654 inline void Game_System::IncSaveCount() {
655 	++data.save_count;
656 }
657 
GetSystem2Name()658 inline StringView Game_System::GetSystem2Name() {
659 	return dbsys->system2_name;
660 }
661 
GetCurrentBGM()662 inline const lcf::rpg::Music& Game_System::GetCurrentBGM() {
663 	return data.current_music;
664 }
665 
MemorizeBGM()666 inline void Game_System::MemorizeBGM() {
667 	data.stored_music = data.current_music;
668 }
669 
PlayMemorizedBGM()670 inline void Game_System::PlayMemorizedBGM() {
671 	BgmPlay(data.stored_music);
672 }
673 
SetAllowTeleport(bool allow)674 inline void Game_System::SetAllowTeleport(bool allow) {
675 	data.teleport_allowed = allow;
676 }
677 
GetAllowTeleport()678 inline bool Game_System::GetAllowTeleport() {
679 	return data.teleport_allowed;
680 }
681 
SetAllowEscape(bool allow)682 inline void Game_System::SetAllowEscape(bool allow) {
683 	data.escape_allowed = allow;
684 }
685 
GetAllowEscape()686 inline bool Game_System::GetAllowEscape() {
687 	return data.escape_allowed;
688 }
689 
SetAllowSave(bool allow)690 inline void Game_System::SetAllowSave(bool allow) {
691 	data.save_allowed = allow;
692 }
693 
GetAllowSave()694 inline bool Game_System::GetAllowSave() {
695 	return data.save_allowed;
696 }
697 
SetAllowMenu(bool allow)698 inline void Game_System::SetAllowMenu(bool allow) {
699 	data.menu_allowed = allow;
700 }
701 
GetAllowMenu()702 inline bool Game_System::GetAllowMenu() {
703 	return data.menu_allowed;
704 }
705 
706 
707 #endif
708