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 <cstdlib>
20 #include <algorithm>
21 #include <iomanip>
22 #include <iostream>
23 #include <sstream>
24 #include <cassert>
25 #include "audio.h"
26 #include "game_map.h"
27 #include "game_battle.h"
28 #include "game_event.h"
29 #include "game_player.h"
30 #include "game_switches.h"
31 #include "game_variables.h"
32 #include "game_party.h"
33 #include "game_actors.h"
34 #include "game_system.h"
35 #include "game_message.h"
36 #include "game_screen.h"
37 #include "spriteset_map.h"
38 #include "sprite_character.h"
39 #include "scene_map.h"
40 #include "scene_battle.h"
41 #include "scene_menu.h"
42 #include "scene_save.h"
43 #include "scene_load.h"
44 #include "scene_name.h"
45 #include "scene_shop.h"
46 #include "scene_gameover.h"
47 #include "scene.h"
48 #include "graphics.h"
49 #include "input.h"
50 #include "main_data.h"
51 #include "output.h"
52 #include "player.h"
53 #include "util_macro.h"
54 #include "game_interpreter_map.h"
55 #include <lcf/reader_lcf.h>
56 
57 enum EnemyEncounterSubcommand {
58 	eOptionEnemyEncounterVictory = 0,
59 	eOptionEnemyEncounterEscape = 1,
60 	eOptionEnemyEncounterDefeat = 2,
61 };
62 
63 enum ShopSubcommand {
64 	eOptionShopTransaction = 0,
65 	eOptionShopNoTransaction = 1,
66 };
67 
68 enum InnSubcommand {
69 	eOptionInnStay = 0,
70 	eOptionInnNoStay = 1,
71 };
72 
SetState(const lcf::rpg::SaveEventExecState & save)73 void Game_Interpreter_Map::SetState(const lcf::rpg::SaveEventExecState& save) {
74 	Clear();
75 	_state = save;
76 	_keyinput.fromSave(save);
77 }
78 
OnMapChange()79 void Game_Interpreter_Map::OnMapChange() {
80 	// When we change the map, we reset all event id's to 0.
81 	for (auto& frame: _state.stack) {
82 		frame.event_id = 0;
83 	}
84 }
85 
86 /**
87  * Execute Command.
88  */
ExecuteCommand()89 bool Game_Interpreter_Map::ExecuteCommand() {
90 	auto& frame = GetFrame();
91 	const auto& com = frame.commands[frame.current_command];
92 
93 	switch (static_cast<Cmd>(com.code)) {
94 		case Cmd::RecallToLocation:
95 			return CommandRecallToLocation(com);
96 		case Cmd::EnemyEncounter:
97 			return CommandEnemyEncounter(com);
98 		case Cmd::VictoryHandler:
99 			return CommandVictoryHandler(com);
100 		case Cmd::EscapeHandler:
101 			return CommandEscapeHandler(com);
102 		case Cmd::DefeatHandler:
103 			return CommandDefeatHandler(com);
104 		case Cmd::EndBattle:
105 			return CommandEndBattle(com);
106 		case Cmd::OpenShop:
107 			return CommandOpenShop(com);
108 		case Cmd::Transaction:
109 			return CommandTransaction(com);
110 		case Cmd::NoTransaction:
111 			return CommandNoTransaction(com);
112 		case Cmd::EndShop:
113 			return CommandEndShop(com);
114 		case Cmd::ShowInn:
115 			return CommandShowInn(com);
116 		case Cmd::Stay:
117 			return CommandStay(com);
118 		case Cmd::NoStay:
119 			return CommandNoStay(com);
120 		case Cmd::EndInn:
121 			return CommandEndInn(com);
122 		case Cmd::EnterHeroName:
123 			return CommandEnterHeroName(com);
124 		case Cmd::Teleport:
125 			return CommandTeleport(com);
126 		case Cmd::EnterExitVehicle:
127 			return CommandEnterExitVehicle(com);
128 		case Cmd::PanScreen:
129 			return CommandPanScreen(com);
130 		case Cmd::ShowBattleAnimation:
131 			return CommandShowBattleAnimation(com);
132 		case Cmd::FlashSprite:
133 			return CommandFlashSprite(com);
134 		case Cmd::ProceedWithMovement:
135 			return CommandProceedWithMovement(com);
136 		case Cmd::HaltAllMovement:
137 			return CommandHaltAllMovement(com);
138 		case Cmd::PlayMovie:
139 			return CommandPlayMovie(com);
140 		case Cmd::OpenSaveMenu:
141 			return CommandOpenSaveMenu(com);
142 		case Cmd::OpenMainMenu:
143 			return CommandOpenMainMenu(com);
144 		case Cmd::OpenLoadMenu:
145 			return CommandOpenLoadMenu(com);
146 		case Cmd::ToggleAtbMode:
147 			return CommandToggleAtbMode(com);
148 		case Cmd::OpenVideoOptions:
149 			return CommandOpenVideoOptions(com);
150 		default:
151 			return Game_Interpreter::ExecuteCommand();
152 	}
153 }
154 
155 /**
156  * Commands
157  */
CommandRecallToLocation(lcf::rpg::EventCommand const & com)158 bool Game_Interpreter_Map::CommandRecallToLocation(lcf::rpg::EventCommand const& com) { // Code 10830
159 	if (Game_Message::IsMessageActive()) {
160 		return false;
161 	}
162 
163 	auto& frame = GetFrame();
164 	auto& index = frame.current_command;
165 
166 	int var_map_id = com.parameters[0];
167 	int var_x = com.parameters[1];
168 	int var_y = com.parameters[2];
169 	int map_id = Main_Data::game_variables->Get(var_map_id);
170 	int x = Main_Data::game_variables->Get(var_x);
171 	int y = Main_Data::game_variables->Get(var_y);
172 
173 	auto tt = main_flag ? TeleportTarget::eForegroundTeleport : TeleportTarget::eParallelTeleport;
174 
175 	Main_Data::game_player->ReserveTeleport(map_id, x, y, -1, tt);
176 
177 	// Parallel events should keep on running in 2k and 2k3, unlike in later versions
178 	if (!main_flag)
179 		return true;
180 
181 	index++;
182 	return false;
183 }
184 
CommandEnemyEncounter(lcf::rpg::EventCommand const & com)185 bool Game_Interpreter_Map::CommandEnemyEncounter(lcf::rpg::EventCommand const& com) { // code 10710
186 	auto& frame = GetFrame();
187 	auto& index = frame.current_command;
188 
189 	if (Game_Message::IsMessageActive()) {
190 		return false;
191 	}
192 
193 	BattleArgs args;
194 
195 	args.troop_id = ValueOrVariable(com.parameters[0], com.parameters[1]);
196 
197 	switch (com.parameters[2]) {
198 	case 0:
199 		Game_Map::SetupBattle(args);
200 		break;
201 	case 1:
202 		args.background = ToString(com.string);
203 
204 		if (Player::IsRPG2k3()) {
205 			args.formation = static_cast<lcf::rpg::System::BattleFormation>(com.parameters[7]);
206 		}
207 		break;
208 	case 2:
209 		args.terrain_id = com.parameters[8];
210 		break;
211 	default:
212 		return false;
213 	}
214 	auto escape_mode = com.parameters[3]; // 0 disallow, 1 end event processing, 2 victory/escape custom handler
215 	auto defeat_mode = com.parameters[4]; // 0 game over, 1 victory/defeat custom handler
216 
217 	if (escape_mode == 1) {
218 		_state.abort_on_escape = true;
219 	}
220 
221 	args.allow_escape = (escape_mode != 0);
222 	args.first_strike = com.parameters[5] != 0;
223 
224 	if (Player::IsRPG2k3()) {
225 		args.condition = static_cast<lcf::rpg::System::BattleCondition>(com.parameters[6]);
226 	}
227 
228 	auto indent = com.indent;
229 	auto continuation = [this, indent, defeat_mode](BattleResult result) {
230 		int sub_idx = subcommand_sentinel;
231 
232 		switch (result) {
233 			case BattleResult::Victory:
234 				sub_idx = eOptionEnemyEncounterVictory;
235 				break;
236 			case BattleResult::Escape:
237 				sub_idx = eOptionEnemyEncounterEscape;
238 				if (_state.abort_on_escape) {
239 					return EndEventProcessing();
240 				}
241 				break;
242 			case BattleResult::Defeat:
243 				sub_idx = eOptionEnemyEncounterDefeat;
244 				if (defeat_mode == 0) {
245 					Scene::Push(std::make_shared<Scene_Gameover>());
246 				}
247 				break;
248 			case BattleResult::Abort:
249 				break;
250 		}
251 
252 		SetSubcommandIndex(indent, sub_idx);
253 	};
254 
255 	args.on_battle_end = continuation;
256 
257 	Scene::instance->SetRequestedScene(Scene_Battle::Create(std::move(args)));
258 
259 	// save game compatibility with RPG_RT
260 	ReserveSubcommandIndex(com.indent);
261 
262 	++index;
263 	return false;
264 }
265 
CommandVictoryHandler(lcf::rpg::EventCommand const & com)266 bool Game_Interpreter_Map::CommandVictoryHandler(lcf::rpg::EventCommand const& com) { // code 20710
267 	return CommandOptionGeneric(com, eOptionEnemyEncounterVictory, {Cmd::EscapeHandler, Cmd::DefeatHandler, Cmd::EndBattle});
268 }
269 
CommandEscapeHandler(lcf::rpg::EventCommand const & com)270 bool Game_Interpreter_Map::CommandEscapeHandler(lcf::rpg::EventCommand const& com) { // code 20711
271 	return CommandOptionGeneric(com, eOptionEnemyEncounterEscape, {Cmd::DefeatHandler, Cmd::EndBattle});
272 }
273 
CommandDefeatHandler(lcf::rpg::EventCommand const & com)274 bool Game_Interpreter_Map::CommandDefeatHandler(lcf::rpg::EventCommand const& com) { // code 20712
275 	return CommandOptionGeneric(com, eOptionEnemyEncounterDefeat, {Cmd::EndBattle});
276 }
277 
CommandEndBattle(lcf::rpg::EventCommand const &)278 bool Game_Interpreter_Map::CommandEndBattle(lcf::rpg::EventCommand const& /* com */) { // code 20713
279 	return true;
280 }
281 
CommandOpenShop(lcf::rpg::EventCommand const & com)282 bool Game_Interpreter_Map::CommandOpenShop(lcf::rpg::EventCommand const& com) { // code 10720
283 	auto& frame = GetFrame();
284 	auto& index = frame.current_command;
285 
286 	if (Game_Message::IsMessageActive()) {
287 		return false;
288 	}
289 
290 	bool allow_buy = false;
291 	bool allow_sell = false;
292 
293 	switch (com.parameters[0]) {
294 		case 0:
295 			allow_buy = true;
296 			allow_sell = true;
297 			break;
298 		case 1:
299 			allow_buy = true;
300 			break;
301 		case 2:
302 			allow_sell = true;
303 			break;
304 		default:
305 			break;
306 	}
307 
308 	auto shop_type = com.parameters[1];
309 
310 	// Not used, but left here for documentation purposes
311 	//bool has_shop_handlers = com.parameters[2] != 0;
312 
313 	std::vector<int> goods;
314 	for (auto it = com.parameters.begin() + 4; it < com.parameters.end(); ++it) {
315 		goods.push_back(*it);
316 	}
317 
318 	auto indent = com.indent;
319 	auto continuation = [this, indent](bool did_transaction) {
320 		int sub_idx = did_transaction ? eOptionShopTransaction : eOptionShopNoTransaction;
321 		SetSubcommandIndex(indent, sub_idx);
322 	};
323 
324 	auto scene = std::make_shared<Scene_Shop>(
325 			std::move(goods), shop_type, allow_buy, allow_sell, std::move(continuation));
326 
327 	Scene::instance->SetRequestedScene(std::move(scene));
328 
329 	// save game compatibility with RPG_RT
330 	ReserveSubcommandIndex(com.indent);
331 
332 	++index;
333 	return false;
334 }
335 
CommandTransaction(lcf::rpg::EventCommand const & com)336 bool Game_Interpreter_Map::CommandTransaction(lcf::rpg::EventCommand const& com) { // code 20720
337 	return CommandOptionGeneric(com, eOptionShopTransaction, {Cmd::NoTransaction, Cmd::EndShop});
338 }
339 
CommandNoTransaction(lcf::rpg::EventCommand const & com)340 bool Game_Interpreter_Map::CommandNoTransaction(lcf::rpg::EventCommand const& com) { // code 20721
341 	return CommandOptionGeneric(com, eOptionShopNoTransaction, {Cmd::EndShop});
342 }
343 
CommandEndShop(lcf::rpg::EventCommand const &)344 bool Game_Interpreter_Map::CommandEndShop(lcf::rpg::EventCommand const& /* com */) { // code 20722
345 	return true;
346 }
347 
CommandShowInn(lcf::rpg::EventCommand const & com)348 bool Game_Interpreter_Map::CommandShowInn(lcf::rpg::EventCommand const& com) { // code 10730
349 	int inn_type = com.parameters[0];
350 	auto inn_price = com.parameters[1];
351 	// Not used, but left here for documentation purposes
352 	// bool has_inn_handlers = com.parameters[2] != 0;
353 
354 	if (inn_price == 0) {
355 		if (Game_Message::IsMessageActive()) {
356 			return false;
357 		}
358 
359 		// Skip prompt.
360 		_async_op = ContinuationShowInnStart(com.indent, 0, inn_price);
361 		return true;
362 	}
363 
364 	// Emulates RPG_RT behavior (Bug?) Inn's called by parallel events
365 	// overwrite the current message.
366 	if (main_flag && !Game_Message::CanShowMessage(main_flag)) {
367 		return false;
368 	}
369 
370 	auto pm = PendingMessage();
371 
372 	StringView greeting_1, greeting_2, greeting_3, accept, cancel;
373 
374 	switch (inn_type) {
375 		case 0:
376 			greeting_1 = lcf::Data::terms.inn_a_greeting_1;
377 			greeting_2 = lcf::Data::terms.inn_a_greeting_2;
378 			greeting_3 = lcf::Data::terms.inn_a_greeting_3;
379 			accept = lcf::Data::terms.inn_a_accept;
380 			cancel = lcf::Data::terms.inn_a_cancel;
381 			break;
382 		case 1:
383 			greeting_1 = lcf::Data::terms.inn_b_greeting_1;
384 			greeting_2 = lcf::Data::terms.inn_b_greeting_2;
385 			greeting_3 = lcf::Data::terms.inn_b_greeting_3;
386 			accept = lcf::Data::terms.inn_b_accept;
387 			cancel = lcf::Data::terms.inn_b_cancel;
388 			break;
389 	}
390 
391 	if (Player::IsRPG2kE()) {
392 		auto price_s = std::to_string(inn_price);
393 		pm.PushLine(
394 			Utils::ReplacePlaceholders(
395 				greeting_1,
396 				Utils::MakeArray('V', 'U'),
397 				Utils::MakeSvArray(price_s, lcf::Data::terms.gold)
398 			)
399 		);
400 		pm.PushLine(
401 			Utils::ReplacePlaceholders(
402 				greeting_3,
403 				Utils::MakeArray('V', 'U'),
404 				Utils::MakeSvArray(price_s, lcf::Data::terms.gold)
405 			)
406 		);
407 	}
408 	else {
409 		pm.PushLine(fmt::format("{} {}{} {}", greeting_1, inn_price, lcf::Data::terms.gold, greeting_2));
410 		pm.PushLine(ToString(greeting_3));
411 	}
412 
413 	bool can_afford = (Main_Data::game_party->GetGold() >= inn_price);
414 	pm.SetChoiceResetColors(true);
415 
416 	pm.PushChoice(ToString(accept), can_afford);
417 	pm.PushChoice(ToString(cancel));
418 
419 	pm.SetShowGoldWindow(true);
420 
421 	int indent = com.indent;
422 	pm.SetChoiceContinuation([this, indent, inn_price](int choice_result) {
423 			return ContinuationShowInnStart(indent, choice_result, inn_price);
424 			});
425 
426 	// save game compatibility with RPG_RT
427 	ReserveSubcommandIndex(com.indent);
428 
429 	Game_Message::SetPendingMessage(std::move(pm));
430 	_state.show_message = true;
431 
432 	return true;
433 }
434 
ContinuationShowInnStart(int indent,int choice_result,int price)435 AsyncOp Game_Interpreter_Map::ContinuationShowInnStart(int indent, int choice_result, int price) {
436 	bool inn_stay = (choice_result == 0);
437 
438 	SetSubcommandIndex(indent, inn_stay ? eOptionInnStay : eOptionInnNoStay);
439 
440 	if (inn_stay) {
441 		Main_Data::game_party->GainGold(-price);
442 
443 		return AsyncOp::MakeCallInn();
444 	}
445 	return {};
446 }
447 
CommandStay(lcf::rpg::EventCommand const & com)448 bool Game_Interpreter_Map::CommandStay(lcf::rpg::EventCommand const& com) { // code 20730
449 	return CommandOptionGeneric(com, eOptionInnStay, {Cmd::NoStay, Cmd::EndInn});
450 }
451 
CommandNoStay(lcf::rpg::EventCommand const & com)452 bool Game_Interpreter_Map::CommandNoStay(lcf::rpg::EventCommand const& com) { // code 20731
453 	return CommandOptionGeneric(com, eOptionInnNoStay, {Cmd::EndInn});
454 }
455 
CommandEndInn(lcf::rpg::EventCommand const &)456 bool Game_Interpreter_Map::CommandEndInn(lcf::rpg::EventCommand const& /* com */) { // code 20732
457 	return true;
458 }
459 
CommandEnterHeroName(lcf::rpg::EventCommand const & com)460 bool Game_Interpreter_Map::CommandEnterHeroName(lcf::rpg::EventCommand const& com) { // code 10740
461 	auto& frame = GetFrame();
462 	auto& index = frame.current_command;
463 
464 	if (Game_Message::IsMessageActive()) {
465 		return false;
466 	}
467 
468 	auto actor_id = com.parameters[0];
469 	auto charset = com.parameters[1];
470 	auto use_default_name = com.parameters[2];
471 
472 	auto scene = std::make_shared<Scene_Name>(actor_id, charset, use_default_name);
473 	Scene::instance->SetRequestedScene(std::move(scene));
474 
475 	++index;
476 	return false;
477 }
478 
CommandTeleport(lcf::rpg::EventCommand const & com)479 bool Game_Interpreter_Map::CommandTeleport(lcf::rpg::EventCommand const& com) { // Code 10810
480 																		   // TODO: if in battle return true
481 	if (Game_Message::IsMessageActive()) {
482 		return false;
483 	}
484 
485 	auto& frame = GetFrame();
486 	auto& index = frame.current_command;
487 
488 	int map_id = com.parameters[0];
489 	int x = com.parameters[1];
490 	int y = com.parameters[2];
491 
492 	// RPG2k3 feature
493 	int direction = com.parameters.size() > 3 ? com.parameters[3] - 1 : -1;
494 
495 	auto tt = main_flag ? TeleportTarget::eForegroundTeleport : TeleportTarget::eParallelTeleport;
496 
497 	Main_Data::game_player->ReserveTeleport(map_id, x, y, direction, tt);
498 
499 	// Parallel events should keep on running in 2k and 2k3, unlike in later versions
500 	if (!main_flag)
501 		return true;
502 
503 	index++;
504 	return false;
505 }
506 
CommandEnterExitVehicle(lcf::rpg::EventCommand const &)507 bool Game_Interpreter_Map::CommandEnterExitVehicle(lcf::rpg::EventCommand const& /* com */) { // code 10840
508 	Main_Data::game_player->GetOnOffVehicle();
509 
510 	return true;
511 }
512 
CommandPanScreen(lcf::rpg::EventCommand const & com)513 bool Game_Interpreter_Map::CommandPanScreen(lcf::rpg::EventCommand const& com) { // code 11060
514 	int direction;
515 	int distance;
516 	int speed;
517 	bool waiting_pan_screen = false;
518 
519 	auto& player = *Main_Data::game_player;
520 
521 	switch (com.parameters[0]) {
522 	case 0: // Lock
523 		player.LockPan();
524 		break;
525 	case 1: // Unlock
526 		player.UnlockPan();
527 		break;
528 	case 2: // Pan
529 		direction = com.parameters[1];
530 		distance = com.parameters[2];
531 		// FIXME: For an "instant pan" Yume2kki passes a huge value (53) here
532 		// which crashes depending on the hardware
533 		speed = Utils::Clamp<int>(com.parameters[3], 1, 6);
534 		waiting_pan_screen = com.parameters[4] != 0;
535 		player.StartPan(direction, distance, speed);
536 		break;
537 	case 3: // Reset
538 		speed = Utils::Clamp<int>(com.parameters[3], 1, 6);
539 		waiting_pan_screen = com.parameters[4] != 0;
540 		player.ResetPan(speed);
541 		distance = std::max(
542 				std::abs(player.GetPanX() - player.GetTargetPanX())
543 				, std::abs(player.GetPanY() - player.GetTargetPanY()));
544 		distance /= SCREEN_TILE_SIZE;
545 		break;
546 	}
547 
548 	if (waiting_pan_screen) {
549 		// RPG_RT uses the max wait for all pending pan commands, not just the current one.
550 		_state.wait_time = player.GetPanWait();
551 	}
552 
553 	return true;
554 }
555 
CommandShowBattleAnimation(lcf::rpg::EventCommand const & com)556 bool Game_Interpreter_Map::CommandShowBattleAnimation(lcf::rpg::EventCommand const& com) { // code 11210
557 	int animation_id = com.parameters[0];
558 	int evt_id = com.parameters[1];
559 	bool waiting_battle_anim = com.parameters[2] > 0;
560 	bool global = com.parameters[3] > 0;
561 
562 	Game_Character* chara = GetCharacter(evt_id);
563 	if (chara == NULL)
564 		return true;
565 
566 	if (evt_id == Game_Character::CharThisEvent)
567 		evt_id = GetThisEventId();
568 
569 	int frames = Main_Data::game_screen->ShowBattleAnimation(animation_id, evt_id, global);
570 
571 	if (waiting_battle_anim) {
572 		_state.wait_time = frames;
573 	}
574 
575 	return true;
576 }
577 
CommandFlashSprite(lcf::rpg::EventCommand const & com)578 bool Game_Interpreter_Map::CommandFlashSprite(lcf::rpg::EventCommand const& com) { // code 11320
579 	int event_id = com.parameters[0];
580 	int r = com.parameters[1];
581 	int g = com.parameters[2];
582 	int b = com.parameters[3];
583 	int p = com.parameters[4];
584 
585 	int tenths = com.parameters[5];
586 	bool wait = com.parameters[6] > 0;
587 	Game_Character* event = GetCharacter(event_id);
588 
589 	if (event != NULL) {
590 		event->Flash(r, g, b, p, tenths * DEFAULT_FPS / 10);
591 
592 		if (wait) {
593 			SetupWait(tenths);
594 		}
595 	}
596 
597 	return true;
598 }
599 
CommandProceedWithMovement(lcf::rpg::EventCommand const &)600 bool Game_Interpreter_Map::CommandProceedWithMovement(lcf::rpg::EventCommand const& /* com */) { // code 11340
601 	_state.wait_movement = true;
602 	return true;
603 }
604 
CommandHaltAllMovement(lcf::rpg::EventCommand const &)605 bool Game_Interpreter_Map::CommandHaltAllMovement(lcf::rpg::EventCommand const& /* com */) { // code 11350
606 	Game_Map::RemoveAllPendingMoves();
607 	return true;
608 }
609 
CommandPlayMovie(lcf::rpg::EventCommand const & com)610 bool Game_Interpreter_Map::CommandPlayMovie(lcf::rpg::EventCommand const& com) { // code 11560
611 	if (Game_Message::IsMessageActive()) {
612 		return false;
613 	}
614 
615 	auto filename = ToString(com.string);
616 	int pos_x = ValueOrVariable(com.parameters[0], com.parameters[1]);
617 	int pos_y = ValueOrVariable(com.parameters[0], com.parameters[2]);
618 	int res_x = com.parameters[3];
619 	int res_y = com.parameters[4];
620 
621 	Output::Warning("Couldn't play movie: {}. Movie playback is not implemented (yet).", filename);
622 
623 	Main_Data::game_screen->PlayMovie(filename, pos_x, pos_y, res_x, res_y);
624 
625 	return true;
626 }
627 
CommandOpenSaveMenu(lcf::rpg::EventCommand const &)628 bool Game_Interpreter_Map::CommandOpenSaveMenu(lcf::rpg::EventCommand const& /* com */) { // code 11910
629 	auto& frame = GetFrame();
630 	auto& index = frame.current_command;
631 
632 	if (Game_Message::IsMessageActive()) {
633 		return false;
634 	}
635 
636 	Scene::instance->SetRequestedScene(std::make_shared<Scene_Save>());
637 	++index;
638 	return false;
639 }
640 
CommandOpenMainMenu(lcf::rpg::EventCommand const &)641 bool Game_Interpreter_Map::CommandOpenMainMenu(lcf::rpg::EventCommand const& /* com */) { // code 11950
642 	auto& frame = GetFrame();
643 	auto& index = frame.current_command;
644 
645 	if (Game_Message::IsMessageActive()) {
646 		return false;
647 	}
648 
649 	Scene::instance->SetRequestedScene(std::make_shared<Scene_Menu>());
650 	++index;
651 	return false;
652 }
653 
CommandOpenLoadMenu(lcf::rpg::EventCommand const &)654 bool Game_Interpreter_Map::CommandOpenLoadMenu(lcf::rpg::EventCommand const& /* com */) {
655 	auto& frame = GetFrame();
656 	auto& index = frame.current_command;
657 
658 	if (Game_Message::IsMessageActive()) {
659 		return false;
660 	}
661 
662 	Scene::instance->SetRequestedScene(std::make_shared<Scene_Load>());
663 	++index;
664 	return false;
665 }
666 
CommandToggleAtbMode(lcf::rpg::EventCommand const &)667 bool Game_Interpreter_Map::CommandToggleAtbMode(lcf::rpg::EventCommand const& /* com */) {
668 	Main_Data::game_system->ToggleAtbMode();
669 	return true;
670 }
671 
CommandOpenVideoOptions(lcf::rpg::EventCommand const &)672 bool Game_Interpreter_Map::CommandOpenVideoOptions(lcf::rpg::EventCommand const& /* com */) {
673 	if (Game_Message::IsMessageActive()) {
674 		return false;
675 	}
676 
677 	Output::Warning("OpenVideoOptions: Command not supported");
678 	return true;
679 }
680 
681