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 <vector>
21 #include <sstream>
22 #include <cmath>
23 #include <iomanip>
24 #include "baseui.h"
25 #include "cache.h"
26 #include "input.h"
27 #include "game_variables.h"
28 #include "game_switches.h"
29 #include "game_map.h"
30 #include "game_system.h"
31 #include "game_battle.h"
32 #include "scene_debug.h"
33 #include "scene_load.h"
34 #include "scene_save.h"
35 #include "scene_map.h"
36 #include "scene_battle.h"
37 #include "player.h"
38 #include "window_command.h"
39 #include "window_varlist.h"
40 #include "window_numberinput.h"
41 #include "bitmap.h"
42 #include "game_party.h"
43 #include "game_player.h"
44 #include <lcf/data.h>
45 #include "output.h"
46 #include "transition.h"
47 
48 namespace {
49 struct IndexSet {
50 	int range_index = 0;
51 	int range_page = 0;
52 	int range_page_index = 0;
53 };
54 
55 std::array<IndexSet,Scene_Debug::eLastMainMenuOption> prev = {};
56 }
57 
58 constexpr int arrow_animation_frames = 20;
59 
Scene_Debug()60 Scene_Debug::Scene_Debug() {
61 	Scene::type = Scene::Debug;
62 }
63 
ResetPrevIndices()64 void Scene_Debug::ResetPrevIndices() {
65 	prev = {};
66 }
67 
Start()68 void Scene_Debug::Start() {
69 	CreateRangeWindow();
70 	CreateVarListWindow();
71 	CreateNumberInputWindow();
72 
73 	SetupUiRangeList();
74 
75 	range_window->SetActive(true);
76 	var_window->SetActive(false);
77 
78 	UpdateRangeListWindow();
79 	var_window->Refresh();
80 }
81 
GetFrame(int n)82 Scene_Debug::StackFrame& Scene_Debug::GetFrame(int n) {
83 	auto i = stack_index - n;
84 	assert(i >= 0 && i < static_cast<int>(stack.size()));
85 	return stack[i];
86 }
87 
GetFrame(int n) const88 const Scene_Debug::StackFrame& Scene_Debug::GetFrame(int n) const {
89 	auto i = stack_index - n;
90 	assert(i >= 0 && i < static_cast<int>(stack.size()));
91 	return stack[i];
92 }
93 
GetStackSize() const94 int Scene_Debug::GetStackSize() const {
95 	return stack_index + 1;
96 }
97 
GetWindowMode() const98 Window_VarList::Mode Scene_Debug::GetWindowMode() const {
99 	switch (mode) {
100 		case eSwitch:
101 			return Window_VarList::eSwitch;
102 		case eVariable:
103 			return Window_VarList::eVariable;
104 		case eItem:
105 			return Window_VarList::eItem;
106 		case eBattle:
107 			return Window_VarList::eTroop;
108 		case eMap:
109 			return Window_VarList::eMap;
110 		case eFullHeal:
111 			return Window_VarList::eHeal;
112 		case eLevel:
113 			return Window_VarList::eLevel;
114 		case eCallCommonEvent:
115 			return Window_VarList::eCommonEvent;
116 		case eCallMapEvent:
117 			return Window_VarList::eMapEvent;
118 		default:
119 			return Window_VarList::eNone;
120 	}
121 }
122 
UpdateFrameValueFromUi()123 void Scene_Debug::UpdateFrameValueFromUi() {
124 	auto& frame = GetFrame();
125 	auto& idx = prev[mode];
126 	switch (frame.uimode) {
127 		case eUiMain:
128 			idx.range_index = range_index;
129 			idx.range_page = range_page;
130 			break;
131 		case eUiRangeList:
132 			idx.range_index = range_index;
133 			idx.range_page = range_page;
134 			frame.value = range_page * 100 + range_index * 10;
135 			break;
136 		case eUiVarList:
137 			idx.range_page_index = var_window->GetIndex();
138 			frame.value = range_page * 100 + range_index * 10 + var_window->GetIndex() + 1;
139 			break;
140 		case eUiNumberInput:
141 			frame.value = numberinput_window->GetNumber();
142 			break;
143 	}
144 }
145 
Push(UiMode ui)146 void Scene_Debug::Push(UiMode ui) {
147 	++stack_index;
148 	assert(stack_index < static_cast<int>(stack.size()));
149 	stack[stack_index] = { ui, 0 };
150 
151 	range_window->SetActive(false);
152 	var_window->SetActive(false);
153 	numberinput_window->SetActive(false);
154 	numberinput_window->SetVisible(false);
155 }
156 
SetupUiRangeList()157 void Scene_Debug::SetupUiRangeList() {
158 	auto& idx = prev[mode];
159 	auto vmode = GetWindowMode();
160 
161 	range_index = idx.range_index;
162 	range_page = idx.range_page;
163 
164 	var_window->SetMode(vmode);
165 	var_window->UpdateList(range_page * 100 + range_index * 10 + 1);
166 
167 	range_window->SetIndex(range_index);
168 }
169 
PushUiRangeList()170 void Scene_Debug::PushUiRangeList() {
171 	Push(eUiRangeList);
172 
173 	SetupUiRangeList();
174 
175 	range_window->SetActive(true);
176 
177 	UpdateRangeListWindow();
178 	var_window->Refresh();
179 
180 }
181 
PushUiVarList()182 void Scene_Debug::PushUiVarList() {
183 	const bool was_range_list = (GetFrame().uimode == eUiRangeList);
184 
185 	Push(eUiVarList);
186 
187 	auto& idx = prev[mode];
188 
189 	if (!was_range_list) {
190 		SetupUiRangeList();
191 	}
192 
193 	var_window->SetActive(true);
194 	var_window->SetIndex(idx.range_page_index);
195 
196 	UpdateRangeListWindow();
197 	var_window->Refresh();
198 
199 }
200 
PushUiNumberInput(int init_value,int digits,bool show_operator)201 void Scene_Debug::PushUiNumberInput(int init_value, int digits, bool show_operator) {
202 	Push(eUiNumberInput);
203 
204 	numberinput_window->SetNumber(init_value);
205 	numberinput_window->SetShowOperator(show_operator);
206 	numberinput_window->SetVisible(true);
207 	numberinput_window->SetActive(true);
208 	numberinput_window->SetMaxDigits(digits);
209 	numberinput_window->Refresh();
210 
211 	var_window->Refresh();
212 	UpdateRangeListWindow();
213 }
214 
Pop()215 void Scene_Debug::Pop() {
216 	auto pui = GetFrame().uimode;
217 
218 	if (pui == eUiVarList) {
219 		var_window->SetIndex(-1);
220 	}
221 
222 	range_window->SetActive(false);
223 	var_window->SetActive(false);
224 	numberinput_window->SetActive(false);
225 	numberinput_window->SetVisible(false);
226 
227 	if (stack_index == 0) {
228 		Scene::Pop();
229 		return;
230 	}
231 
232 	--stack_index;
233 
234 	auto nui = GetFrame().uimode;
235 	switch (nui) {
236 		case eUiMain:
237 			var_window->SetMode(Window_VarList::eNone);
238 			range_index = (static_cast<int>(mode) - 1) % 10;
239 			range_page = (static_cast<int>(mode) - 1) / 10;
240 			range_window->SetActive(true);
241 			range_window->SetIndex(range_index);
242 			break;
243 		case eUiRangeList:
244 			range_window->SetActive(true);
245 			range_index = (GetFrame().value % 100) / 10;
246 			range_page = GetFrame().value / 100;
247 			range_window->SetIndex(range_index);
248 			break;
249 		case eUiVarList:
250 			var_window->SetActive(true);
251 			var_window->SetIndex((GetFrame().value - 1) % 10);
252 			break;
253 		case eUiNumberInput:
254 			numberinput_window->SetNumber(GetFrame().value);
255 			numberinput_window->SetActive(true);
256 			numberinput_window->SetVisible(true);
257 			break;
258 	}
259 
260 	if (stack_index == 0) {
261 		mode = eMain;
262 	}
263 
264 	UpdateRangeListWindow();
265 	var_window->Refresh();
266 }
267 
Update()268 void Scene_Debug::Update() {
269 	range_window->Update();
270 	if (range_index != range_window->GetIndex()){
271 		range_index = range_window->GetIndex();
272 		var_window->UpdateList(range_page * 100 + range_index * 10 + 1);
273 		var_window->Refresh();
274 	}
275 	var_window->Update();
276 
277 	if (numberinput_window->GetActive())
278 		numberinput_window->Update();
279 
280 	if (Input::IsTriggered(Input::CANCEL)) {
281 		UpdateFrameValueFromUi();
282 		Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
283 		Pop();
284 	} else if (Input::IsTriggered(Input::DECISION)) {
285 		UpdateFrameValueFromUi();
286 		if (mode == eMain) {
287 			auto next_mode = static_cast<Mode>(range_window->GetIndex() + range_page * 10 + 1);
288 			if (next_mode > eMain && next_mode < eLastMainMenuOption) {
289 				const auto is_battle = Game_Battle::IsBattleRunning();
290 				if (
291 						(is_battle && (next_mode == eSave || next_mode == eBattle || next_mode == eMap || next_mode == eCallMapEvent))
292 						|| (!is_battle && (next_mode == eCallBattleEvent))
293 				   )
294 				{
295 					Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Buzzer));
296 				} else {
297 					Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
298 					mode = next_mode;
299 				}
300 			}
301 		}
302 
303 		const auto sz = GetStackSize();
304 		const auto& frame = GetFrame();
305 		switch (mode) {
306 			case eMain:
307 			case eLastMainMenuOption:
308 				break;
309 			case eSave:
310 				Scene::PopUntil(Scene::Map);
311 				Scene::Push(std::make_shared<Scene_Save>());
312 				break;
313 			case eLoad:
314 				Scene::Push(std::make_shared<Scene_Load>());
315 				mode = eMain;
316 				break;
317 			case eSwitch:
318 				if (sz > 2) {
319 					DoSwitch();
320 				} else if (sz > 1) {
321 					PushUiVarList();
322 				} else if (sz > 0) {
323 					PushUiRangeList();
324 				}
325 				break;
326 			case eVariable:
327 				if (sz > 3) {
328 					DoVariable();
329 				} else if (sz > 2) {
330 					if (Main_Data::game_variables->IsValid(frame.value)) {
331 						PushUiNumberInput(Main_Data::game_variables->Get(frame.value), Main_Data::game_variables->GetMaxDigits(), true);
332 					}
333 				} else if (sz > 1) {
334 					PushUiVarList();
335 				} else {
336 					PushUiRangeList();
337 				}
338 				break;
339 			case eGold:
340 				if (sz > 1) {
341 					DoGold();
342 				} else {
343 					PushUiNumberInput(Main_Data::game_party->GetGold(), 6, false);
344 					range_index = 0;
345 					range_window->SetIndex(range_index);
346 				}
347 				break;
348 			case eItem:
349 				if (sz > 3) {
350 					DoItem();
351 				} else if (sz > 2) {
352 					if (frame.value <= static_cast<int>(lcf::Data::items.size())) {
353 						PushUiNumberInput(Main_Data::game_party->GetItemCount(frame.value), Main_Data::game_party->GetMaxItemCount(frame.value) >= 100 ? 3 : 2, false);
354 					}
355 				} else if (sz > 1) {
356 					PushUiVarList();
357 				} else {
358 					PushUiRangeList();
359 				}
360 				break;
361 			case eBattle:
362 				if (sz > 2) {
363 					DoBattle();
364 				} else if (sz > 1) {
365 					PushUiVarList();
366 				} else {
367 					PushUiRangeList();
368 				}
369 				break;
370 			case eMap:
371 				if (sz > 4) {
372 					DoMap();
373 				} else if (sz > 3) {
374 					// FIXME: Remember previous y
375 					PushUiNumberInput(GetFrame(-1).value, 4, false);
376 				} else if (sz > 2) {
377 					const auto map_id = GetFrame().value;
378 					if (IsValidMapId(map_id)) {
379 						// Reset x and y values
380 						GetFrame(-1).value = 0;
381 						GetFrame(-2).value = 0;
382 						PushUiNumberInput(GetFrame(-1).value, 4, false);
383 					}
384 				} else if (sz > 1) {
385 					PushUiVarList();
386 				} else {
387 					PushUiRangeList();
388 				}
389 				break;
390 			case eFullHeal:
391 				if (sz > 1) {
392 					DoFullHeal();
393 				} else {
394 					PushUiVarList();
395 				}
396 				break;
397 			case eLevel:
398 				if (sz > 2) {
399 					DoLevel();
400 				} else if (sz > 1) {
401 					if (frame.value <= static_cast<int>(Main_Data::game_party->GetActors().size())) {
402 						auto* actor = Main_Data::game_party->GetActors()[frame.value - 1];
403 						PushUiNumberInput(actor->GetLevel(), actor->GetMaxLevel() >= 100 ? 3 : 2, false);
404 					}
405 				} else {
406 					PushUiVarList();
407 				}
408 				break;
409 			case eCallCommonEvent:
410 				if (sz > 2) {
411 					DoCallCommonEvent();
412 				} else if (sz > 1) {
413 					PushUiVarList();
414 				} else {
415 					PushUiRangeList();
416 				}
417 				break;
418 			case eCallMapEvent:
419 				if (sz > 3) {
420 					DoCallMapEvent();
421 				} else if (sz > 2) {
422 					auto* event = Game_Map::GetEvent(GetFrame().value);
423 					if (event) {
424 						const auto num_digits = static_cast<int>(std::log10(event->GetNumPages()) + 1);
425 						PushUiNumberInput(1, num_digits, false);
426 					}
427 				} else if (sz > 1) {
428 					PushUiVarList();
429 				} else {
430 					PushUiRangeList();
431 				}
432 				break;
433 			case eCallBattleEvent:
434 				if (sz > 1) {
435 					DoCallBattleEvent();
436 				} else {
437 					auto* troop = Game_Battle::GetActiveTroop();
438 					if (troop) {
439 						const auto num_digits = static_cast<int>(std::log10(troop->pages.size()) + 1);
440 						PushUiNumberInput(0, num_digits, false);
441 					}
442 				}
443 				break;
444 		}
445 		Game_Map::SetNeedRefresh(true);
446 	} else if (range_window->GetActive() && Input::IsRepeated(Input::RIGHT)) {
447 		if (range_page < GetLastPage()) {
448 			++range_page;
449 		} else {
450 			range_page = 0;
451 		}
452 		var_window->UpdateList(range_page * 100 + range_index * 10 + 1);
453 		UpdateRangeListWindow();
454 		var_window->Refresh();
455 	} else if (range_window->GetActive() && Input::IsRepeated(Input::LEFT)) {
456 		if (range_page > 0) {
457 			--range_page;
458 		} else {
459 			range_page = GetLastPage();
460 		}
461 		var_window->UpdateList(range_page * 100 + range_index * 10 + 1);
462 		UpdateRangeListWindow();
463 		var_window->Refresh();
464 	}
465 
466 	UpdateArrows();
467 }
468 
CreateRangeWindow()469 void Scene_Debug::CreateRangeWindow() {
470 
471 	std::vector<std::string> ranges;
472 	for (int i = 0; i < 10; i++)
473 		ranges.push_back("");
474 	range_window.reset(new Window_Command(ranges, 96));
475 
476 	range_window->SetHeight(176);
477 	range_window->SetY(32);
478 }
479 
UpdateRangeListWindow()480 void Scene_Debug::UpdateRangeListWindow() {
481 	int idx = 0;
482 	const bool is_battle = Game_Battle::IsBattleRunning();
483 
484 	auto addItem = [&](const auto& name, bool enabled = true) {
485 		range_window->SetItemText(idx, name);
486 		if (!enabled) {
487 			range_window->DisableItem(idx);
488 		}
489 		++idx;
490 	};
491 
492 	auto fillRange = [&](const auto& prefix) {
493 		for (int i = 0; i < 10; i++){
494 			const auto st = range_page * 100 + i * 10 + 1;
495 			addItem(fmt::format("{}[{:04d}-{:04d}]", prefix, st, st + 9));
496 		}
497 	};
498 
499 	switch (mode) {
500 		case eMain:
501 			if (range_page == 0) {
502 				addItem("Save", !is_battle);
503 				addItem("Load");
504 				addItem("Switches");
505 				addItem("Variables");
506 				addItem(lcf::Data::terms.gold.c_str());
507 				addItem("Items");
508 				addItem("Battle", !is_battle);
509 				addItem("Goto Map", !is_battle);
510 				addItem("Full Heal");
511 				addItem("Level");
512 			} else {
513 				addItem("Call ComEvent");
514 				addItem("Call MapEvent", !is_battle);
515 				addItem("Call BtlEvent", is_battle);
516 			}
517 			break;
518 		case eSwitch:
519 			fillRange("Sw");
520 			break;
521 		case eVariable:
522 			fillRange("Vr");
523 			break;
524 		case eItem:
525 			fillRange("It");
526 			break;
527 		case eBattle:
528 			fillRange("Bt");
529 			break;
530 		case eMap:
531 			if (GetStackSize() > 3) {
532 				if (GetStackSize() > 4) {
533 					addItem("Map: " + std::to_string(GetFrame(2).value));
534 					addItem("X: " + std::to_string(GetFrame(1).value));
535 					addItem("Y: ");
536 				} else {
537 					addItem("Map: " + std::to_string(GetFrame(1).value));
538 					addItem("X: ");
539 				}
540 			} else {
541 				fillRange("Mp");
542 			}
543 			break;
544 		case eCallCommonEvent:
545 			fillRange("Ce");
546 			break;
547 		case eCallMapEvent:
548 			if (GetStackSize() > 3) {
549 				auto* event = Game_Map::GetEvent(GetFrame(1).value);
550 				if (event) {
551 					addItem(fmt::format("{:04d}: {}", event->GetId(), event->GetName()));
552 					addItem(fmt::format("NumPages: {}", event->GetNumPages()));
553 					const auto* page = event->GetActivePage();
554 					const auto page_id = page ? page->ID : 0;
555 					addItem(fmt::format("ActvPage: {}", page_id));
556 					addItem(fmt::format("Enabled: {}", event->IsActive() ? 'Y' : 'N'));
557 					addItem(fmt::format("X: {}", event->GetX()));
558 					addItem(fmt::format("Y: {}", event->GetY()));
559 				}
560 			} else {
561 				fillRange("Me");
562 			}
563 			break;
564 		case eGold:
565 			addItem(lcf::Data::terms.gold);
566 			for (int i = 1; i < 10; i++){
567 				range_window->SetItemText(i, "");
568 			}
569 			break;
570 		case eFullHeal:
571 			addItem("Full Heal");
572 			break;
573 		case eLevel:
574 			addItem("Level");
575 			break;
576 		case eCallBattleEvent:
577 			if (is_battle) {
578 				auto* troop = Game_Battle::GetActiveTroop();
579 				if (troop) {
580 					addItem(troop->name);
581 					addItem(fmt::format("TroopId: {}", troop->ID));
582 					addItem(fmt::format("NumEnemies: {}", troop->members.size()));
583 					addItem(fmt::format("NumPages: {}", troop->pages.size()));
584 				}
585 			}
586 			break;
587 		default:
588 			break;
589 	}
590 
591 	while (idx < 10) {
592 		addItem("", true);
593 	}
594 }
595 
CreateVarListWindow()596 void Scene_Debug::CreateVarListWindow() {
597 	std::vector<std::string> vars;
598 	for (int i = 0; i < 10; i++)
599 		vars.push_back("");
600 	var_window.reset(new Window_VarList(vars));
601 	var_window->SetX(range_window->GetWidth());
602 	var_window->SetY(range_window->GetY());
603 	var_window->SetVisible(false);
604 	var_window->SetIndex(-1);
605 
606 	var_window->UpdateList(range_page * 100 + range_index * 10 + 1);
607 }
608 
CreateNumberInputWindow()609 void Scene_Debug::CreateNumberInputWindow() {
610 	numberinput_window.reset(new Window_NumberInput(160 - (Main_Data::game_variables->GetMaxDigits() + 1) * 6 - 8, 104,
611 		(Main_Data::game_variables->GetMaxDigits() + 1) * 12 + 16, 32));
612 	numberinput_window->SetVisible(false);
613 	numberinput_window->SetOpacity(255);
614 	numberinput_window->SetShowOperator(true);
615 }
616 
GetNumMainMenuItems() const617 int Scene_Debug::GetNumMainMenuItems() const {
618 	return static_cast<int>(eLastMainMenuOption) - 1;
619 }
620 
GetLastPage()621 int Scene_Debug::GetLastPage() {
622 	size_t num_elements = 0;
623 	switch (mode) {
624 		case eMain:
625 			return GetNumMainMenuItems() / 10;
626 		case eSwitch:
627 			num_elements = Main_Data::game_switches->GetSize();
628 			break;
629 		case eVariable:
630 			num_elements = Main_Data::game_variables->GetSize();
631 			break;
632 		case eItem:
633 			num_elements = lcf::Data::items.size();
634 			break;
635 		case eBattle:
636 			num_elements = lcf::Data::troops.size();
637 			break;
638 		case eMap:
639 			num_elements = lcf::Data::treemap.maps.size() > 0 ? lcf::Data::treemap.maps.back().ID : 0;
640 			break;
641 		case eFullHeal:
642 			num_elements = Main_Data::game_party->GetBattlerCount() + 1;
643 			break;
644 		case eLevel:
645 			num_elements = Main_Data::game_party->GetBattlerCount();
646 			break;
647 		case eCallCommonEvent:
648 			num_elements = lcf::Data::commonevents.size();
649 			break;
650 		case eCallMapEvent:
651 			num_elements = Game_Map::GetHighestEventId();
652 			break;
653 		default:
654 			break;
655 	}
656 
657 	if (num_elements > 0) {
658 		return (static_cast<int>(num_elements) - 1) / 100;
659 	}
660 	return 0;
661 }
662 
IsValidMapId(int map_id) const663 bool Scene_Debug::IsValidMapId(int map_id) const {
664 	auto iter = std::lower_bound(lcf::Data::treemap.maps.begin(), lcf::Data::treemap.maps.end(), map_id,
665 			[](const lcf::rpg::MapInfo& l, int r) { return l.ID < r; });
666 	return (iter != lcf::Data::treemap.maps.end()
667 			&& iter->ID == map_id
668 			&& iter->type == lcf::rpg::TreeMap::MapType_map);
669 }
670 
DoSwitch()671 void Scene_Debug::DoSwitch() {
672 	const auto sw_id = GetFrame().value;
673 	if (Main_Data::game_switches->IsValid(sw_id)) {
674 		Main_Data::game_switches->Flip(sw_id);
675 		Game_Map::SetNeedRefresh(true);
676 
677 		var_window->Refresh();
678 	}
679 }
680 
DoVariable()681 void Scene_Debug::DoVariable() {
682 	const auto var_id = GetFrame(1).value;
683 	const auto value = GetFrame(0).value;
684 	Main_Data::game_variables->Set(var_id, value);
685 	Game_Map::SetNeedRefresh(true);
686 
687 	Pop();
688 }
689 
DoGold()690 void Scene_Debug::DoGold() {
691 	const auto delta = GetFrame().value - Main_Data::game_party->GetGold();
692 	Main_Data::game_party->GainGold(delta);
693 
694 	Pop();
695 }
696 
DoItem()697 void Scene_Debug::DoItem() {
698 	const auto item_id = GetFrame(1).value;
699 	auto delta = GetFrame().value - Main_Data::game_party->GetItemCount(item_id);
700 
701 	Main_Data::game_party->AddItem(item_id, delta);
702 
703 	Game_Map::SetNeedRefresh(true);
704 
705 	Pop();
706 }
707 
DoBattle()708 void Scene_Debug::DoBattle() {
709 	auto troop_id = GetFrame(0).value;
710 	if (troop_id > static_cast<int>(lcf::Data::troops.size())) {
711 		return;
712 	}
713 
714 	Scene::PopUntil(Scene::Map);
715 	if (!Scene::instance) {
716 		return;
717 	}
718 
719 	BattleArgs args;
720 	args.troop_id = troop_id;
721 	args.first_strike = false;
722 	args.allow_escape = true;
723 
724 	Output::Debug("Debug Scene starting battle {}.", troop_id);
725 
726 	Game_Map::SetupBattle(args);
727 
728 	Scene::Push(Scene_Battle::Create(std::move(args)));
729 }
730 
DoMap()731 void Scene_Debug::DoMap() {
732 	auto y = GetFrame(0).value;
733 	auto x = GetFrame(1).value;
734 	auto map_id = GetFrame(2).value;
735 
736 	Scene::PopUntil(Scene::Map);
737 	if (Scene::instance) {
738 		Main_Data::game_player->ReserveTeleport(map_id, x, y, -1, TeleportTarget::eSkillTeleport);
739 	}
740 }
741 
DoFullHeal()742 void Scene_Debug::DoFullHeal() {
743 	const auto id = GetFrame(0).value;
744 
745 	Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_UseItem));
746 	auto actors = Main_Data::game_party->GetActors();
747 	if (id <= 1) {
748 		for (auto& actor: actors) {
749 			actor->FullHeal();
750 		}
751 	} else {
752 		int idx = id - 2;
753 		if (idx < static_cast<int>(actors.size())) {
754 			actors[idx]->FullHeal();
755 		}
756 	}
757 	var_window->UpdateList(1);
758 	var_window->Refresh();
759 }
760 
DoLevel()761 void Scene_Debug::DoLevel() {
762 	const auto id = GetFrame(1).value;
763 	const auto level = GetFrame(0).value;
764 
765 	auto actors = Main_Data::game_party->GetActors();
766 	int idx = id - 1;
767 	if (idx < static_cast<int>(actors.size())) {
768 		if (actors[idx]->GetLevel() != level) {
769 			actors[idx]->ChangeLevel(level, nullptr);
770 		}
771 	}
772 
773 	Pop();
774 }
775 
DoCallCommonEvent()776 void Scene_Debug::DoCallCommonEvent() {
777 	const auto ceid = GetFrame(0).value;
778 
779 	if (ceid > static_cast<int>(lcf::Data::commonevents.size())) {
780 		return;
781 	}
782 
783 	auto& ce = Game_Map::GetCommonEvents()[ceid - 1];
784 
785 	if (Game_Battle::IsBattleRunning()) {
786 		Game_Battle::GetInterpreter().Push(&ce);
787 		Scene::PopUntil(Scene::Battle);
788 		Output::Debug("Debug Scene Forced execution of common event {} on the battle foreground interpreter.", ce.GetIndex());
789 	} else {
790 		Game_Map::GetInterpreter().Push(&ce);
791 		Scene::PopUntil(Scene::Map);
792 		Output::Debug("Debug Scene Forced execution of common event {} on the map foreground interpreter.", ce.GetIndex());
793 	}
794 }
795 
DoCallMapEvent()796 void Scene_Debug::DoCallMapEvent() {
797 	if (Game_Battle::IsBattleRunning()) {
798 		return;
799 	}
800 
801 	const auto me_id = GetFrame(1).value;
802 	const auto page_id = GetFrame(0).value;
803 
804 	auto* me = Game_Map::GetEvent(me_id);
805 	if (!me) {
806 		return;
807 	}
808 
809 	auto* page = me->GetPage(page_id);
810 	if (!page) {
811 		return;
812 	}
813 
814 	Game_Map::GetInterpreter().Push(me, page, false);
815 	Scene::PopUntil(Scene::Map);
816 	Output::Debug("Debug Scene Forced execution of map event {} page {} on the map foreground interpreter.", me->GetId(), page->ID);
817 }
818 
DoCallBattleEvent()819 void Scene_Debug::DoCallBattleEvent() {
820 	if (!Game_Battle::IsBattleRunning()) {
821 		return;
822 	}
823 
824 	auto* troop = Game_Battle::GetActiveTroop();
825 	if (!troop) {
826 		return;
827 	}
828 
829 	const auto page_idx = GetFrame(0).value - 1;
830 
831 	if (page_idx < 0 || page_idx >= static_cast<int>(troop->pages.size())) {
832 		return;
833 	}
834 
835 	auto& page = troop->pages[page_idx];
836 
837 	Game_Battle::GetInterpreter().Push(page.event_commands, 0, false);
838 	Scene::PopUntil(Scene::Battle);
839 	Output::Debug("Debug Scene Forced execution of battle troop {} event page {} on the map foreground interpreter.", troop->ID, page.ID);
840 }
841 
TransitionIn(SceneType)842 void Scene_Debug::TransitionIn(SceneType /* prev_scene */) {
843 	Transition::instance().InitShow(Transition::TransitionCutIn, this);
844 }
845 
TransitionOut(SceneType)846 void Scene_Debug::TransitionOut(SceneType /* next_scene */) {
847 	Transition::instance().InitErase(Transition::TransitionCutOut, this);
848 }
849 
UpdateArrows()850 void Scene_Debug::UpdateArrows() {
851 	bool show_left_arrow = (range_page > 0);
852 	bool show_right_arrow = (range_page < GetLastPage());
853 
854 	if (show_left_arrow || show_right_arrow) {
855 		arrow_frame = (arrow_frame + 1) % (arrow_animation_frames * 2);
856 	}
857 	bool arrow_visible = (arrow_frame < arrow_animation_frames);
858 	range_window->SetLeftArrow(show_left_arrow && arrow_visible);
859 	range_window->SetRightArrow(show_right_arrow && arrow_visible);
860 }
861