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 #include <algorithm>
19 #include <sstream>
20 #include "input.h"
21 #include "player.h"
22 #include "sprite.h"
23 #include "sprite_enemy.h"
24 #include "game_battler.h"
25 #include "game_system.h"
26 #include "game_party.h"
27 #include "game_enemy.h"
28 #include "game_enemyparty.h"
29 #include "game_message.h"
30 #include "game_battle.h"
31 #include "game_battlealgorithm.h"
32 #include "game_screen.h"
33 #include "battle_animation.h"
34 #include <lcf/reader_util.h>
35 #include "scene_battle_rpg2k.h"
36 #include "scene_battle.h"
37 #include "scene_gameover.h"
38 #include "game_interpreter_battle.h"
39 #include "output.h"
40 #include "rand.h"
41 #include "autobattle.h"
42 #include "enemyai.h"
43 #include "battle_message.h"
44 
Scene_Battle_Rpg2k(const BattleArgs & args)45 Scene_Battle_Rpg2k::Scene_Battle_Rpg2k(const BattleArgs& args) :
46 	Scene_Battle(args)
47 {
48 }
49 
~Scene_Battle_Rpg2k()50 Scene_Battle_Rpg2k::~Scene_Battle_Rpg2k() {
51 }
52 
Start()53 void Scene_Battle_Rpg2k::Start() {
54 	Scene_Battle::Start();
55 	CreateEnemySprites();
56 }
57 
CreateUi()58 void Scene_Battle_Rpg2k::CreateUi() {
59 	Scene_Battle::CreateUi();
60 
61 	status_window.reset(new Window_BattleStatus(0, (SCREEN_TARGET_HEIGHT-80), SCREEN_TARGET_WIDTH - option_command_mov, 80));
62 
63 	CreateBattleTargetWindow();
64 	CreateBattleCommandWindow();
65 
66 	battle_message_window.reset(new Window_BattleMessage(0, (SCREEN_TARGET_HEIGHT - 80), SCREEN_TARGET_WIDTH, 80));
67 
68 	if (!IsEscapeAllowed()) {
69 		auto it = std::find(battle_options.begin(), battle_options.end(), Escape);
70 		if (it != battle_options.end()) {
71 			options_window->DisableItem(std::distance(battle_options.begin(), it));
72 		}
73 	}
74 
75 	SetCommandWindows(0);
76 
77 	ResetWindows(true);
78 	battle_message_window->SetVisible(true);
79 }
80 
CreateEnemySprites()81 void Scene_Battle_Rpg2k::CreateEnemySprites() {
82 	for (auto* enemy: Main_Data::game_enemyparty->GetEnemies()) {
83 		auto sprite = std::make_unique<Sprite_Enemy>(enemy);
84 		sprite->SetVisible(true);
85 		enemy->SetBattleSprite(std::move(sprite));
86 	}
87 }
88 
GetEnemyTargetNames()89 static std::vector<std::string> GetEnemyTargetNames() {
90 	std::vector<std::string> commands;
91 
92 	std::vector<Game_Battler*> enemies;
93 	Main_Data::game_enemyparty->GetActiveBattlers(enemies);
94 
95 	for (auto& enemy: enemies) {
96 		commands.push_back(ToString(enemy->GetName()));
97 	}
98 
99 	return commands;
100 }
101 
CreateBattleTargetWindow()102 void Scene_Battle_Rpg2k::CreateBattleTargetWindow() {
103 	auto commands = GetEnemyTargetNames();
104 	target_window.reset(new Window_Command(std::move(commands), 136, 4));
105 	target_window->SetHeight(80);
106 	target_window->SetY(SCREEN_TARGET_HEIGHT-80);
107 	// Above other windows
108 	target_window->SetZ(Priority_Window + 10);
109 }
110 
RefreshTargetWindow()111 void Scene_Battle_Rpg2k::RefreshTargetWindow() {
112 	auto commands = GetEnemyTargetNames();
113 	target_window->ReplaceCommands(std::move(commands));
114 }
115 
CreateBattleCommandWindow()116 void Scene_Battle_Rpg2k::CreateBattleCommandWindow() {
117 	std::vector<std::string> commands = {
118 		ToString(lcf::Data::terms.command_attack),
119 		ToString(lcf::Data::terms.command_skill),
120 		ToString(lcf::Data::terms.command_defend),
121 		ToString(lcf::Data::terms.command_item)
122 	};
123 
124 	command_window.reset(new Window_Command(std::move(commands), 76));
125 	command_window->SetHeight(80);
126 	command_window->SetX(SCREEN_TARGET_WIDTH - option_command_mov);
127 	command_window->SetY(SCREEN_TARGET_HEIGHT-80);
128 }
129 
RefreshCommandWindow()130 void Scene_Battle_Rpg2k::RefreshCommandWindow() {
131 	command_window->SetItemText(1, active_actor->GetSkillName());
132 }
133 
SetState(Scene_Battle::State new_state)134 void Scene_Battle_Rpg2k::SetState(Scene_Battle::State new_state) {
135 	previous_state = state;
136 	state = new_state;
137 
138 	SetSceneActionSubState(0);
139 }
140 
UpdateBattleState()141 bool Scene_Battle_Rpg2k::UpdateBattleState() {
142 	if (resume_from_debug_scene) {
143 		resume_from_debug_scene = false;
144 		return true;
145 	}
146 
147 	UpdateScreen();
148 	UpdateBattlers();
149 	UpdateUi();
150 	battle_message_window->Update();
151 
152 	if (!UpdateEvents()) {
153 		return false;
154 	}
155 
156 	if (!UpdateTimers()) {
157 		return false;
158 	}
159 
160 	if (Input::IsTriggered(Input::DEBUG_MENU)) {
161 		if (this->CallDebug()) {
162 			// Set this flag so that when we return and run update again, we resume exactly from after this point.
163 			resume_from_debug_scene = true;
164 			return false;
165 		}
166 	}
167 	return true;
168 }
169 
Update()170 void Scene_Battle_Rpg2k::Update() {
171 	const auto process_scene = UpdateBattleState();
172 
173 	while (process_scene) {
174 		// Something ended the battle.
175 		if (Scene::instance.get() != this) {
176 			break;
177 		}
178 
179 		if (IsWindowMoving()) {
180 			break;
181 		}
182 
183 		if (Game_Message::IsMessageActive() || Game_Battle::GetInterpreter().IsRunning()) {
184 			break;
185 		}
186 
187 		if (!CheckWait()) {
188 			break;
189 		}
190 
191 		if (ProcessSceneAction() == SceneActionReturn::eWaitTillNextFrame) {
192 			break;
193 		}
194 	}
195 
196 	Game_Battle::UpdateGraphics();
197 }
198 
SetSceneActionSubState(int substate)199 void Scene_Battle_Rpg2k::SetSceneActionSubState(int substate) {
200 	scene_action_substate = substate;
201 }
202 
NextTurn()203 void Scene_Battle_Rpg2k::NextTurn() {
204 	Main_Data::game_party->IncTurns();
205 	Game_Battle::GetInterpreterBattle().ResetPagesExecuted();
206 }
207 
CheckBattleEndAndScheduleEvents()208 bool Scene_Battle_Rpg2k::CheckBattleEndAndScheduleEvents() {
209 	if (CheckBattleEndConditions()) {
210 		return false;
211 	}
212 
213 	auto& interp = Game_Battle::GetInterpreterBattle();
214 
215 	int page = interp.ScheduleNextPage(nullptr);
216 #ifdef EP_DEBUG_BATTLE2K_STATE_MACHINE
217 	if (page) {
218 		Output::Debug("Battle2k ScheduleNextEventPage Scheduled Page {} frame={}", page, Main_Data::game_system->GetFrameCounter());
219 	} else {
220 		Output::Debug("Battle2k ScheduleNextEventPage No Events to Run frame={}", Main_Data::game_system->GetFrameCounter());
221 	}
222 #else
223 	(void)page;
224 #endif
225 
226 	return !interp.IsRunning();
227 }
228 
229 
ProcessSceneAction()230 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneAction() {
231 #ifdef EP_DEBUG_BATTLE2K_STATE_MACHINE
232 	static int last_state = -1;
233 	static int last_substate = -1;
234 	if (state != last_state || scene_action_substate != last_substate) {
235 		Output::Debug("Battle2k ProcessSceneAction({},{}) frames={}", state, scene_action_substate, Main_Data::game_system->GetFrameCounter());
236 		last_state = state;
237 		last_substate = scene_action_substate;
238 	}
239 #endif
240 	switch (state) {
241 		case State_Start:
242 			return ProcessSceneActionStart();
243 		case State_SelectOption:
244 			return ProcessSceneActionFightAutoEscape();
245 		case State_SelectActor:
246 			return ProcessSceneActionActor();
247 		case State_AutoBattle:
248 			return ProcessSceneActionAutoBattle();
249 		case State_SelectCommand:
250 			return ProcessSceneActionCommand();
251 		case State_SelectItem:
252 			return ProcessSceneActionItem();
253 		case State_SelectSkill:
254 			return ProcessSceneActionSkill();
255 		case State_SelectEnemyTarget:
256 			return ProcessSceneActionEnemyTarget();
257 		case State_SelectAllyTarget:
258 			return ProcessSceneActionAllyTarget();
259 		case State_Battle:
260 			return ProcessSceneActionBattle();
261 		case State_Victory:
262 			return ProcessSceneActionVictory();
263 		case State_Defeat:
264 			return ProcessSceneActionDefeat();
265 		case State_Escape:
266 			return ProcessSceneActionEscape();
267 	}
268 	assert(false && "Invalid SceneActionState!");
269 	return SceneActionReturn::eWaitTillNextFrame;
270 }
271 
ProcessSceneActionStart()272 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionStart() {
273 	enum SubState {
274 		eBegin,
275 		eDisplayMonsters,
276 		eFirstStrike,
277 		eClear
278 	};
279 
280 	if (scene_action_substate == eBegin) {
281 		ResetWindows(true);
282 		battle_message_window->SetVisible(true);
283 
284 		std::vector<Game_Battler *> visible_enemies;
285 		// First time entered, initialize.
286 		Main_Data::game_enemyparty->GetActiveBattlers(visible_enemies);
287 
288 		for (auto& enemy: visible_enemies) {
289 			// Format and wordwrap all messages, then pull them out and push them back 1 at a time.
290 			battle_message_window->PushWithSubject(lcf::Data::terms.encounter, enemy->GetName());
291 		}
292 
293 		battle_result_messages = battle_message_window->GetLines();
294 		battle_result_messages_it = battle_result_messages.begin();
295 		battle_message_window->Clear();
296 
297 		if (!visible_enemies.empty()) {
298 			SetWait(4, 4);
299 		}
300 		SetSceneActionSubState(eDisplayMonsters);
301 		return SceneActionReturn::eContinueThisFrame;
302 	}
303 
304 	if (scene_action_substate == eDisplayMonsters) {
305 		if (battle_result_messages_it == battle_result_messages.end()) {
306 			SetSceneActionSubState(eFirstStrike);
307 			return SceneActionReturn::eContinueThisFrame;
308 		}
309 
310 		if (battle_message_window->IsPageFilled()) {
311 			battle_message_window->Clear();
312 			SetWait(4, 4);
313 			return SceneActionReturn::eContinueThisFrame;
314 		}
315 
316 		battle_message_window->Push(*battle_result_messages_it);
317 		++battle_result_messages_it;
318 
319 		if (battle_result_messages_it == battle_result_messages.end() ||
320 				battle_message_window->IsPageFilled()) {
321 			SetWait(30, 70);
322 		}
323 		else {
324 			SetWait(8, 8);
325 		}
326 
327 		return SceneActionReturn::eContinueThisFrame;
328 	}
329 
330 	if (scene_action_substate == eFirstStrike) {
331 		battle_message_window->Clear();
332 		battle_result_messages.clear();
333 		battle_result_messages_it = battle_result_messages.end();
334 
335 		if (first_strike) {
336 			battle_message_window->Push(lcf::Data::terms.special_combat);
337 			SetWait(30, 70);
338 		}
339 
340 		SetSceneActionSubState(eClear);
341 
342 		return SceneActionReturn::eContinueThisFrame;
343 	}
344 
345 	if (scene_action_substate == eClear) {
346 		battle_message_window->Clear();
347 		SetState(State_SelectOption);
348 	}
349 
350 	return SceneActionReturn::eContinueThisFrame;
351 }
352 
ResetWindows(bool make_invisible)353 void Scene_Battle_Rpg2k::ResetWindows(bool make_invisible) {
354 	options_window->SetActive(false);
355 	status_window->SetActive(false);
356 	command_window->SetActive(false);
357 	item_window->SetActive(false);
358 	skill_window->SetActive(false);
359 	target_window->SetActive(false);
360 	battle_message_window->SetActive(false);
361 
362 	if (!make_invisible) {
363 		return;
364 	}
365 
366 	options_window->SetVisible(false);
367 	status_window->SetVisible(false);
368 	command_window->SetVisible(false);
369 	target_window->SetVisible(false);
370 	battle_message_window->SetVisible(false);
371 	item_window->SetVisible(false);
372 	skill_window->SetVisible(false);
373 	help_window->SetVisible(false);
374 }
375 
SetCommandWindows(int x)376 void Scene_Battle_Rpg2k::SetCommandWindows(int x) {
377 	options_window->SetX(x);
378 	x += options_window->GetWidth();
379 	status_window->SetX(x);
380 	x += status_window->GetWidth();
381 	command_window->SetX(x);
382 }
383 
MoveCommandWindows(int x,int frames)384 void Scene_Battle_Rpg2k::MoveCommandWindows(int x, int frames) {
385 	options_window->InitMovement(options_window->GetX(), options_window->GetY(),
386 			x, options_window->GetY(), frames);
387 
388 	x += options_window->GetWidth();
389 	status_window->InitMovement(status_window->GetX(), status_window->GetY(),
390 			x, status_window->GetY(), frames);
391 
392 	x += status_window->GetWidth();
393 	command_window->InitMovement(command_window->GetX(), command_window->GetY(),
394 			x, command_window->GetY(), frames);
395 }
396 
ProcessSceneActionFightAutoEscape()397 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionFightAutoEscape() {
398 	enum SubState {
399 		eBegin,
400 		eCheckEvents,
401 		eMoveWindow,
402 		eWaitForInput,
403 	};
404 
405 	if (scene_action_substate == eBegin) {
406 		ResetWindows(true);
407 		battle_message_window->SetVisible(true);
408 
409 		SetSceneActionSubState(eCheckEvents);
410 	}
411 
412 	if (scene_action_substate == eCheckEvents) {
413 		if (!CheckBattleEndAndScheduleEvents()) {
414 			return SceneActionReturn::eContinueThisFrame;
415 		}
416 
417 		// No Auto battle/Escape when all actors are sleeping or similar
418 		if (!Main_Data::game_party->IsAnyControllable()) {
419 			SetState(State_SelectActor);
420 			return SceneActionReturn::eContinueThisFrame;
421 		}
422 
423 		SetSceneActionSubState(eMoveWindow);
424 	}
425 
426 	if (scene_action_substate == eMoveWindow) {
427 		options_window->SetVisible(true);
428 		status_window->SetVisible(true);
429 		status_window->SetIndex(-1);
430 		command_window->SetIndex(-1);
431 		command_window->SetVisible(true);
432 		battle_message_window->SetVisible(false);
433 		status_window->Refresh();
434 
435 		if (previous_state == State_SelectCommand) {
436 			MoveCommandWindows(0, 8);
437 		} else {
438 			SetCommandWindows(0);
439 		}
440 		SetSceneActionSubState(eWaitForInput);
441 		return SceneActionReturn::eContinueThisFrame;
442 	}
443 
444 	if (scene_action_substate == eWaitForInput) {
445 		options_window->SetActive(true);
446 
447 		if (Input::IsTriggered(Input::DECISION)) {
448 			if (!message_window->IsVisible()) {
449 				switch (battle_options[options_window->GetIndex()]) {
450 					case Battle: // Battle
451 						Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
452 						RefreshTargetWindow();
453 						target_window->SetVisible(false);
454 						SetState(State_SelectActor);
455 						break;
456 					case AutoBattle: // Auto Battle
457 						SetState(State_AutoBattle);
458 						Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
459 						break;
460 					case Escape: // Escape
461 						if (!IsEscapeAllowed()) {
462 							Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Buzzer));
463 						}
464 						else {
465 							Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
466 							SetState(State_Escape);
467 						}
468 						break;
469 				}
470 			}
471 			return SceneActionReturn::eWaitTillNextFrame;
472 		}
473 	}
474 	return SceneActionReturn::eWaitTillNextFrame;
475 }
476 
ProcessSceneActionActor()477 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionActor() {
478 	SelectNextActor(false);
479 	return SceneActionReturn::eContinueThisFrame;
480 }
481 
ProcessSceneActionAutoBattle()482 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionAutoBattle() {
483 	SelectNextActor(true);
484 	return SceneActionReturn::eContinueThisFrame;
485 }
486 
ProcessSceneActionCommand()487 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionCommand() {
488 	enum SubState {
489 		eMoveWindow,
490 		eWaitForInput,
491 	};
492 
493 	if (scene_action_substate == eMoveWindow) {
494 		RefreshCommandWindow();
495 
496 		ResetWindows(true);
497 
498 		options_window->SetVisible(true);
499 		status_window->SetVisible(true);
500 		command_window->SetVisible(true);
501 		if (previous_state == State_SelectActor) {
502 			command_window->SetIndex(0);
503 		}
504 
505 		MoveCommandWindows(-options_window->GetWidth(), 8);
506 		SetSceneActionSubState(eWaitForInput);
507 		return SceneActionReturn::eContinueThisFrame;
508 	}
509 
510 	if (scene_action_substate == eWaitForInput) {
511 		command_window->SetActive(true);
512 		if (Input::IsTriggered(Input::DECISION)) {
513 			switch (command_window->GetIndex()) {
514 				case 0: // Attack
515 					AttackSelected();
516 					break;
517 				case 1: // Skill
518 					Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
519 					SetState(State_SelectSkill);
520 					break;
521 				case 2: // Defense
522 					DefendSelected();
523 					break;
524 				case 3: // Item
525 					Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
526 					SetState(State_SelectItem);
527 					break;
528 				default:
529 					// no-op
530 					break;
531 			}
532 			return SceneActionReturn::eWaitTillNextFrame;
533 		}
534 		if (Input::IsTriggered(Input::CANCEL)) {
535 			Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
536 			--actor_index;
537 			SelectPreviousActor();
538 			return SceneActionReturn::eWaitTillNextFrame;
539 		}
540 	}
541 	return SceneActionReturn::eWaitTillNextFrame;
542 }
543 
ProcessSceneActionItem()544 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionItem() {
545 	enum SubState {
546 		eBegin,
547 		eWaitForInput,
548 	};
549 
550 	if (scene_action_substate == eBegin) {
551 		ResetWindows(true);
552 
553 		item_window->SetVisible(true);
554 		item_window->SetActive(true);
555 		item_window->SetActor(active_actor);
556 		item_window->Refresh();
557 		item_window->SetHelpWindow(help_window.get());
558 		help_window->SetVisible(true);
559 
560 		SetSceneActionSubState(eWaitForInput);
561 	}
562 
563 	if (scene_action_substate == eWaitForInput) {
564 		if (Input::IsTriggered(Input::DECISION)) {
565 			ItemSelected();
566 			return SceneActionReturn::eWaitTillNextFrame;
567 		}
568 		if (Input::IsTriggered(Input::CANCEL)) {
569 			Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
570 			SetState(State_SelectCommand);
571 			return SceneActionReturn::eWaitTillNextFrame;
572 		}
573 	}
574 	return SceneActionReturn::eWaitTillNextFrame;
575 }
576 
ProcessSceneActionSkill()577 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionSkill() {
578 	enum SubState {
579 		eBegin,
580 		eWaitForInput,
581 	};
582 
583 	if (scene_action_substate == eBegin) {
584 		ResetWindows(true);
585 
586 		skill_window->SetActive(true);
587 		skill_window->SetActor(active_actor->GetId());
588 		if (previous_state == State_SelectCommand) {
589 			skill_window->RestoreActorIndex(actor_index - 1);
590 		}
591 		skill_window->SetVisible(true);
592 		skill_window->SetHelpWindow(help_window.get());
593 		help_window->SetVisible(true);
594 
595 		SetSceneActionSubState(eWaitForInput);
596 	}
597 
598 	if (scene_action_substate == eWaitForInput) {
599 		if (Input::IsTriggered(Input::DECISION)) {
600 			skill_window->SaveActorIndex(actor_index - 1);
601 			SkillSelected();
602 			return SceneActionReturn::eWaitTillNextFrame;
603 		}
604 		if (Input::IsTriggered(Input::CANCEL)) {
605 			Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
606 			skill_window->SaveActorIndex(actor_index - 1);
607 			SetState(State_SelectCommand);
608 			return SceneActionReturn::eWaitTillNextFrame;
609 		}
610 	}
611 	return SceneActionReturn::eWaitTillNextFrame;
612 }
613 
ProcessSceneActionEnemyTarget()614 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionEnemyTarget() {
615 	enum SubState {
616 		eBegin,
617 		eWaitForInput,
618 	};
619 
620 	std::vector<Game_Battler*> enemies;
621 	Main_Data::game_enemyparty->GetActiveBattlers(enemies);
622 	Game_Enemy* target = static_cast<Game_Enemy*>(enemies[target_window->GetIndex()]);
623 
624 	if (scene_action_substate == eBegin) {
625 		select_target_flash_count = 0;
626 		ResetWindows(false);
627 
628 		target_window->SetVisible(true);
629 		target_window->SetActive(true);
630 		target_window->SetVisible(true);
631 		target_window->SetIndex(0);
632 
633 		SetSceneActionSubState(eWaitForInput);
634 	}
635 
636 	++select_target_flash_count;
637 
638 	if (select_target_flash_count == 60) {
639 		SelectionFlash(target);
640 		select_target_flash_count = 0;
641 	}
642 
643 	if (scene_action_substate == eWaitForInput) {
644 		if (Input::IsTriggered(Input::DECISION)) {
645 			EnemySelected();
646 			return SceneActionReturn::eWaitTillNextFrame;
647 		}
648 		if (Input::IsTriggered(Input::CANCEL)) {
649 			Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
650 			SetState(previous_state);
651 			return SceneActionReturn::eWaitTillNextFrame;
652 		}
653 	}
654 	return SceneActionReturn::eWaitTillNextFrame;
655 }
656 
ProcessSceneActionAllyTarget()657 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionAllyTarget() {
658 	enum SubState {
659 		eBegin,
660 		eWaitForInput,
661 	};
662 
663 	if (scene_action_substate == eBegin) {
664 		ResetWindows(false);
665 		status_window->SetActive(true);
666 		status_window->SetVisible(true);
667 		status_window->SetIndex(0);
668 
669 		SetSceneActionSubState(eWaitForInput);
670 	}
671 
672 	if (scene_action_substate == eWaitForInput) {
673 		if (Input::IsTriggered(Input::DECISION)) {
674 			AllySelected();
675 			return SceneActionReturn::eWaitTillNextFrame;
676 		}
677 		if (Input::IsTriggered(Input::CANCEL)) {
678 			Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
679 			SetState(previous_state);
680 			return SceneActionReturn::eWaitTillNextFrame;
681 		}
682 	}
683 	return SceneActionReturn::eWaitTillNextFrame;
684 }
685 
ProcessSceneActionBattle()686 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionBattle() {
687 	enum SubState {
688 		eBegin,
689 		ePreAction,
690 		eBattleAction,
691 		ePost,
692 	};
693 
694 	if (scene_action_substate == eBegin) {
695 		ResetWindows(true);
696 		battle_message_window->SetVisible(true);
697 
698 		SetSceneActionSubState(ePreAction);
699 	}
700 
701 	if (scene_action_substate == ePreAction) {
702 		// Remove actions for battlers who were killed or removed from the battle.
703 		while (!battle_actions.empty() && !battle_actions.front()->Exists()) {
704 			RemoveCurrentAction();
705 		}
706 
707 		// Check for end battle, and run events before action
708 		// This happens before each battler acts and also right after the last battler acts.
709 		if (!CheckBattleEndAndScheduleEvents()) {
710 			return SceneActionReturn::eContinueThisFrame;
711 		}
712 
713 		if (battle_actions.empty()) {
714 			SetSceneActionSubState(ePost);
715 			return SceneActionReturn::eContinueThisFrame;
716 		}
717 
718 		auto* battler = battle_actions.front();
719 		// If we will start a new battle action, first check for state changes
720 		// such as death, paralyze, confuse, etc..
721 		PrepareBattleAction(battler);
722 		pending_battle_action = battler->GetBattleAlgorithm();
723 
724 #ifdef EP_DEBUG_BATTLE2K_STATE_MACHINE
725 		Output::Debug("Battle2k StartBattleAction battler={} frame={}", battler->GetName(), Main_Data::game_system->GetFrameCounter());
726 #endif
727 
728 		// Initialize battle state
729 		battle_action_wait = 0;
730 		SetBattleActionState(BattleActionState_Begin);
731 		battle_action_start_index = 0;
732 		battle_action_results_index = 0;
733 		battle_action_dmg_index = 0;
734 		battle_action_substate_index = 0;
735 		pending_message = {};
736 
737 		SetSceneActionSubState(eBattleAction);
738 	}
739 
740 	if (scene_action_substate == eBattleAction) {
741 		if (ProcessBattleAction(pending_battle_action.get()) == BattleActionReturn::eContinue) {
742 			return SceneActionReturn::eContinueThisFrame;
743 		}
744 
745 		pending_battle_action = nullptr;
746 		RemoveCurrentAction();
747 		battle_message_window->Clear();
748 
749 		SetSceneActionSubState(ePreAction);
750 		return SceneActionReturn::eContinueThisFrame;
751 	}
752 
753 	if (scene_action_substate == ePost) {
754 		// Everybody acted
755 		actor_index = 0;
756 		first_strike = false;
757 
758 		SetState(State_SelectOption);
759 		return SceneActionReturn::eWaitTillNextFrame;
760 	}
761 	return SceneActionReturn::eWaitTillNextFrame;
762 }
763 
ProcessSceneActionVictory()764 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionVictory() {
765 	enum SubState {
766 		eBegin = 0,
767 		eEnd = 1,
768 	};
769 
770 	if (scene_action_substate == eBegin) {
771 		ResetWindows(true);
772 		battle_message_window->Clear();
773 		battle_message_window->SetVisible(true);
774 
775 		int exp = Main_Data::game_enemyparty->GetExp();
776 		int money = Main_Data::game_enemyparty->GetMoney();
777 		std::vector<int> drops;
778 		Main_Data::game_enemyparty->GenerateDrops(drops);
779 
780 		auto pm = PendingMessage();
781 		pm.SetEnableFace(false);
782 
783 		pm.SetWordWrapped(Player::IsRPG2kE());
784 		pm.PushLine(ToString(lcf::Data::terms.victory) + Player::escape_symbol + "|");
785 
786 		std::stringstream ss;
787 		if (exp > 0) {
788 			PushExperienceGainedMessage(pm, exp);
789 		}
790 		if (money > 0) {
791 			PushGoldReceivedMessage(pm, money);
792 		}
793 		PushItemRecievedMessages(pm, drops);
794 
795 		Main_Data::game_system->BgmPlay(Main_Data::game_system->GetSystemBGM(Main_Data::game_system->BGM_Victory));
796 
797 		// Update attributes
798 		std::vector<Game_Battler*> ally_battlers;
799 		Main_Data::game_party->GetActiveBattlers(ally_battlers);
800 
801 		pm.PushPageEnd();
802 
803 		for (auto& ally: ally_battlers) {
804 			Game_Actor* actor = static_cast<Game_Actor*>(ally);
805 			actor->ChangeExp(actor->GetExp() + exp, &pm);
806 		}
807 		Main_Data::game_party->GainGold(money);
808 		for (auto& item: drops) {
809 			Main_Data::game_party->AddItem(item, 1);
810 		}
811 
812 		Game_Message::SetPendingMessage(std::move(pm));
813 
814 		SetSceneActionSubState(eEnd);
815 		return SceneActionReturn::eContinueThisFrame;
816 	}
817 
818 	EndBattle(BattleResult::Victory);
819 	return SceneActionReturn::eWaitTillNextFrame;
820 }
821 
ProcessSceneActionDefeat()822 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionDefeat() {
823 	enum SubState {
824 		eBegin = 0,
825 		eEnd = 1,
826 	};
827 
828 	if (scene_action_substate == eBegin) {
829 		ResetWindows(true);
830 		battle_message_window->Clear();
831 		battle_message_window->SetVisible(true);
832 
833 		Main_Data::game_system->SetMessagePositionFixed(true);
834 		Main_Data::game_system->SetMessagePosition(2);
835 		Main_Data::game_system->SetMessageTransparent(false);
836 
837 		auto pm = PendingMessage();
838 		pm.SetEnableFace(false);
839 
840 		pm.SetWordWrapped(Player::IsRPG2kE());
841 
842 		pm.PushLine(ToString(lcf::Data::terms.defeat));
843 
844 		Main_Data::game_system->BgmPlay(Main_Data::game_system->GetSystemBGM(Main_Data::game_system->BGM_GameOver));
845 
846 		Game_Message::SetPendingMessage(std::move(pm));
847 		SetSceneActionSubState(eEnd);
848 
849 		return SceneActionReturn::eContinueThisFrame;
850 	}
851 
852 	EndBattle(BattleResult::Defeat);
853 	return SceneActionReturn::eWaitTillNextFrame;
854 }
855 
ProcessSceneActionEscape()856 Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionEscape() {
857 	enum SubState {
858 		eBegin = 0,
859 		eSuccess = 1,
860 		eFailure = 2,
861 	};
862 
863 	if (scene_action_substate == eBegin) {
864 		ResetWindows(true);
865 		battle_message_window->Clear();
866 		battle_message_window->SetVisible(true);
867 
868 		auto next_ss = TryEscape() ? eSuccess : eFailure;
869 
870 		if (next_ss == eSuccess) {
871 			battle_message_window->Push(lcf::Data::terms.escape_success);
872 		} else {
873 			battle_message_window->Push(lcf::Data::terms.escape_failure);
874 		}
875 		SetWait(10, 60);
876 		SetSceneActionSubState(next_ss);
877 		return SceneActionReturn::eContinueThisFrame;
878 	}
879 
880 	if (scene_action_substate == eSuccess) {
881 		Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Escape));
882 
883 		EndBattle(BattleResult::Escape);
884 		return SceneActionReturn::eContinueThisFrame;
885 	}
886 
887 	if (scene_action_substate == eFailure) {
888 		SetState(State_Battle);
889 		NextTurn();
890 
891 		CreateEnemyActions();
892 		CreateExecutionOrder();
893 		return SceneActionReturn::eContinueThisFrame;
894 	}
895 	return SceneActionReturn::eWaitTillNextFrame;
896 }
897 
SetBattleActionState(BattleActionState state)898 void Scene_Battle_Rpg2k::SetBattleActionState(BattleActionState state) {
899 	battle_action_state = state;
900 	SetBattleActionSubState(0);
901 }
902 
SetBattleActionSubState(int substate,bool reset_index)903 void Scene_Battle_Rpg2k::SetBattleActionSubState(int substate, bool reset_index) {
904 	battle_action_substate = substate;
905 	if (reset_index) {
906 		battle_action_substate_index = 0;
907 	}
908 }
909 
ProcessBattleAction(Game_BattleAlgorithm::AlgorithmBase * action)910 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleAction(Game_BattleAlgorithm::AlgorithmBase* action) {
911 	if (action == nullptr) {
912 		Output::Warning("ProcessBattleAction: Invalid battle action");
913 		Output::Warning("Please report a bug!");
914 		return BattleActionReturn::eFinished;
915 	}
916 
917 #ifdef EP_DEBUG_BATTLE2K_STATE_MACHINE
918 	static int last_state = -1;
919 	static int last_substate = -1;
920 	static int last_substate_index = -1;
921 	if (battle_action_state != last_state || battle_action_substate != last_substate || battle_action_substate_index != last_substate_index) {
922 		Output::Debug("Battle2k ProcessBattleAction({}, {},{},{}) frames={}", action->GetSource()->GetName(), battle_action_state, battle_action_substate, battle_action_substate_index, Main_Data::game_system->GetFrameCounter());
923 		last_state = battle_action_state;
924 		last_substate = battle_action_substate;
925 		last_substate_index = battle_action_substate_index;
926 	}
927 #endif
928 
929 	switch (battle_action_state) {
930 		case BattleActionState_Begin:
931 			return ProcessBattleActionBegin(action);
932 		case BattleActionState_Usage:
933 			return ProcessBattleActionUsage(action);
934 		case BattleActionState_Animation:
935 			return ProcessBattleActionAnimation(action);
936 		case BattleActionState_AnimationReflect:
937 			return ProcessBattleActionAnimationReflect(action);
938 		case BattleActionState_Execute:
939 			return ProcessBattleActionExecute(action);
940 		case BattleActionState_Critical:
941 			return ProcessBattleActionCritical(action);
942 		case BattleActionState_Apply:
943 			return ProcessBattleActionApply(action);
944 		case BattleActionState_Failure:
945 			return ProcessBattleActionFailure(action);
946 		case BattleActionState_Damage:
947 			return ProcessBattleActionDamage(action);
948 		case BattleActionState_Params:
949 			return ProcessBattleActionParamEffects(action);
950 		case BattleActionState_States:
951 			return ProcessBattleActionStateEffects(action);
952 		case BattleActionState_Attributes:
953 			return ProcessBattleActionAttributeEffects(action);
954 		case BattleActionState_Finished:
955 			return ProcessBattleActionFinished(action);
956 	}
957 
958 	assert(false && "Invalid BattleActionState!");
959 
960 	return BattleActionReturn::eFinished;
961 }
962 
ProcessBattleActionBegin(Game_BattleAlgorithm::AlgorithmBase * action)963 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionBegin(Game_BattleAlgorithm::AlgorithmBase* action) {
964 	enum SubState {
965 		eBegin = 0,
966 		eShowMessage,
967 		ePost,
968 	};
969 
970 	auto* src = action->GetSource();
971 
972 	if (battle_action_substate == eBegin) {
973 		assert(src->Exists());
974 		battle_message_window->Clear();
975 
976 		bool show_message = false;
977 		src->NextBattleTurn();
978 
979 		std::vector<int16_t> states_to_heal = src->BattleStateHeal();
980 		src->ApplyConditions();
981 
982 		const lcf::rpg::State* pri_state = nullptr;
983 		bool pri_was_healed = false;
984 		for (size_t id = 1; id <= lcf::Data::states.size(); ++id) {
985 			auto was_healed = std::find(states_to_heal.begin(), states_to_heal.end(), id) != states_to_heal.end();
986 			if (!was_healed && !src->HasState(id)) {
987 				continue;
988 			}
989 
990 			auto* state = lcf::ReaderUtil::GetElement(lcf::Data::states, id);
991 			if (!pri_state || state->priority >= pri_state->priority) {
992 				pri_state = state;
993 				pri_was_healed = was_healed;
994 			}
995 		}
996 
997 		if (pri_state != nullptr) {
998 			StringView msg = pri_was_healed
999 				? pri_state->message_recovery
1000 				: pri_state->message_affected;
1001 
1002 			// RPG_RT behavior:
1003 			// If state was healed, always prints.
1004 			// If state is inflicted, only prints if msg not empty.
1005 			if (pri_was_healed || !msg.empty()) {
1006 				show_message = true;
1007 				pending_message = ToString(msg);
1008 			}
1009 		}
1010 
1011 		if (action->GetType() != Game_BattleAlgorithm::Type::None || show_message) {
1012 			action->GetSource()->Flash(31, 31, 31, 10, 10);
1013 		}
1014 
1015 		if (show_message) {
1016 			SetWait(4,4);
1017 			SetBattleActionSubState(eShowMessage);
1018 			return BattleActionReturn::eContinue;
1019 		}
1020 		battle_action_substate = ePost;
1021 	}
1022 
1023 	if (battle_action_substate == eShowMessage) {
1024 		battle_message_window->PushWithSubject(std::move(pending_message), action->GetSource()->GetName());
1025 		SetWait(20, 60);
1026 		pending_message.clear();
1027 		SetBattleActionSubState(ePost);
1028 		return BattleActionReturn::eContinue;
1029 	}
1030 
1031 	if (battle_action_substate == ePost) {
1032 		battle_message_window->Clear();
1033 
1034 		if (action->GetType() == Game_BattleAlgorithm::Type::None) {
1035 			SetBattleActionState(BattleActionState_Finished);
1036 			return BattleActionReturn::eContinue;
1037 		}
1038 
1039 		SetWait(4,4);
1040 	}
1041 
1042 	SetBattleActionState(BattleActionState_Usage);
1043 	return BattleActionReturn::eContinue;
1044 }
1045 
1046 
ProcessBattleActionUsage(Game_BattleAlgorithm::AlgorithmBase * action)1047 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionUsage(Game_BattleAlgorithm::AlgorithmBase* action) {
1048 	enum SubState {
1049 		eBegin = 0,
1050 		eMessages,
1051 		eLastMessage,
1052 	};
1053 
1054 	if (battle_action_substate == eBegin) {
1055 		action->Start();
1056 		battle_message_window->Clear();
1057 
1058 		pending_message = action->GetStartMessage(0);
1059 
1060 		SetBattleActionSubState(eMessages);
1061 	}
1062 
1063 	if (battle_action_substate == eMessages) {
1064 		if (!pending_message.empty()) {
1065 			battle_message_window->Push(std::move(pending_message));
1066 			battle_message_window->ScrollToEnd();
1067 
1068 			pending_message = action->GetStartMessage(++battle_action_substate_index);
1069 
1070 			if (!pending_message.empty()) {
1071 				SetWaitForUsage(action->GetType(), 0);
1072 				return BattleActionReturn::eContinue;
1073 			}
1074 		}
1075 
1076 		SetBattleActionSubState(eLastMessage);
1077 	}
1078 
1079 	if (battle_action_substate == eLastMessage) {
1080 		battle_action_start_index = battle_message_window->GetLineCount();
1081 
1082 		auto* se = action->GetStartSe();
1083 		if (se) {
1084 			Main_Data::game_system->SePlay(*se);
1085 		}
1086 	}
1087 
1088 	SetBattleActionState(BattleActionState_Animation);
1089 	return BattleActionReturn::eContinue;
1090 }
1091 
ProcessBattleActionAnimation(Game_BattleAlgorithm::AlgorithmBase * action)1092 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionAnimation(Game_BattleAlgorithm::AlgorithmBase* action) {
1093 	return ProcessBattleActionAnimationImpl(action, false);
1094 }
1095 
ProcessBattleActionAnimationReflect(Game_BattleAlgorithm::AlgorithmBase * action)1096 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionAnimationReflect(Game_BattleAlgorithm::AlgorithmBase* action) {
1097 	return ProcessBattleActionAnimationImpl(action, true);
1098 }
1099 
ProcessBattleActionAnimationImpl(Game_BattleAlgorithm::AlgorithmBase * action,bool reflect)1100 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionAnimationImpl(Game_BattleAlgorithm::AlgorithmBase* action, bool reflect) {
1101 	int frames = 0;
1102 	while(1) {
1103 		const int cur_anim = action->GetAnimationId(battle_action_substate_index);
1104 		++battle_action_substate_index;
1105 		int next_anim = 0;
1106 
1107 		if (cur_anim) {
1108 			if (action->GetTarget()->GetType() == Game_Battler::Type_Enemy) {
1109 				frames = action->PlayAnimation(cur_anim);
1110 			} else {
1111 				frames = action->PlayAnimation(cur_anim, true, 40);
1112 			}
1113 			next_anim = action->GetAnimationId(battle_action_substate_index);
1114 		}
1115 
1116 		if (!next_anim) {
1117 			break;
1118 		}
1119 
1120 		if (frames) {
1121 			SetWait(frames, frames);
1122 			return BattleActionReturn::eContinue;
1123 		}
1124 	}
1125 
1126 	if (!reflect) {
1127 		// Wait for last start message and last animation.
1128 		SetWaitForUsage(action->GetType(), frames);
1129 
1130 		// EasyRPG extension: Support 2k3 reflect feature in 2k battle system.
1131 		if (action->ReflectTargets()) {
1132 			SetBattleActionState(BattleActionState_AnimationReflect);
1133 			return BattleActionReturn::eContinue;
1134 		}
1135 	} else {
1136 		// Wait for reflected animation - no message.
1137 		SetWait(frames, frames);
1138 	}
1139 
1140 	SetBattleActionState(BattleActionState_Execute);
1141 	return BattleActionReturn::eContinue;
1142 }
1143 
ProcessBattleActionExecute(Game_BattleAlgorithm::AlgorithmBase * action)1144 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionExecute(Game_BattleAlgorithm::AlgorithmBase* action) {
1145 	action->Execute();
1146 	if (action->GetType() == Game_BattleAlgorithm::Type::Normal
1147 			|| action->GetType() == Game_BattleAlgorithm::Type::SelfDestruct) {
1148 		SetWait(4,4);
1149 		if (action->IsSuccess() && action->IsCriticalHit()) {
1150 			SetBattleActionState(BattleActionState_Critical);
1151 			return BattleActionReturn::eContinue;
1152 		}
1153 	}
1154 	SetBattleActionState(BattleActionState_Apply);
1155 	return BattleActionReturn::eContinue;
1156 }
1157 
ProcessBattleActionCritical(Game_BattleAlgorithm::AlgorithmBase * action)1158 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionCritical(Game_BattleAlgorithm::AlgorithmBase* action) {
1159 	battle_message_window->Push(BattleMessage::GetCriticalHitMessage(*action->GetSource(), *action->GetTarget()));
1160 	battle_message_window->ScrollToEnd();
1161 	SetWait(10, 30);
1162 
1163 	SetBattleActionState(BattleActionState_Apply);
1164 	return BattleActionReturn::eContinue;
1165 }
1166 
ProcessBattleActionApply(Game_BattleAlgorithm::AlgorithmBase * action)1167 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionApply(Game_BattleAlgorithm::AlgorithmBase* action) {
1168 	action->ApplyCustomEffect();
1169 	action->ApplySwitchEffect();
1170 
1171 	battle_action_results_index = battle_message_window->GetLineCount();
1172 
1173 	if (!action->IsSuccess()) {
1174 		SetBattleActionState(BattleActionState_Failure);
1175 		return BattleActionReturn::eContinue;
1176 	}
1177 
1178 	auto* target = action->GetTarget();
1179 
1180 	if (!target) {
1181 		SetBattleActionState(BattleActionState_Finished);
1182 		return BattleActionReturn::eContinue;
1183 	}
1184 
1185 	SetBattleActionState(BattleActionState_Damage);
1186 	return BattleActionReturn::eContinue;
1187 }
1188 
ProcessBattleActionFailure(Game_BattleAlgorithm::AlgorithmBase * action)1189 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionFailure(Game_BattleAlgorithm::AlgorithmBase* action) {
1190 	enum SubState {
1191 		eBegin = 0,
1192 		eProcess,
1193 	};
1194 
1195 	if (battle_action_substate == eBegin) {
1196 		SetWait(4,4);
1197 		SetBattleActionSubState(eProcess);
1198 		return BattleActionReturn::eContinue;
1199 	}
1200 
1201 	auto* se = action->GetFailureSe();
1202 	if (se) {
1203 		Main_Data::game_system->SePlay(*se);
1204 	}
1205 
1206 	const auto& fail_msg = action->GetFailureMessage();
1207 	battle_message_window->Push(fail_msg);
1208 	battle_message_window->ScrollToEnd();
1209 	SetWait(20, 60);
1210 
1211 	SetBattleActionState(BattleActionState_Finished);
1212 	return BattleActionReturn::eContinue;
1213 }
1214 
ProcessBattleActionDamage(Game_BattleAlgorithm::AlgorithmBase * action)1215 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionDamage(Game_BattleAlgorithm::AlgorithmBase* action) {
1216 	enum SubState {
1217 		eBegin = 0,
1218 		eMessage,
1219 		eApply,
1220 		ePreStates,
1221 		eStates,
1222 		ePost,
1223 	};
1224 
1225 	if (battle_action_substate == eBegin) {
1226 		if (!action->IsAffectHp() || action->GetAffectedHp() > 0 || ((action->IsPositive() || action->IsAbsorbHp()) && action->GetAffectedHp() == 0)) {
1227 			SetBattleActionState(BattleActionState_Params);
1228 			return BattleActionReturn::eContinue;
1229 		}
1230 
1231 		SetWait(4,4);
1232 		SetBattleActionSubState(eMessage);
1233 		return BattleActionReturn::eContinue;
1234 	}
1235 
1236 	if (battle_action_substate == eMessage) {
1237 		auto* target = action->GetTarget();
1238 		assert(target);
1239 		auto dmg = action->GetAffectedHp();
1240 
1241 		if (!action->IsAbsorbHp()) {
1242 			if (target->GetType() == Game_Battler::Type_Ally) {
1243 				Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_AllyDamage));
1244 				if (dmg < 0) {
1245 					Main_Data::game_screen->ShakeOnce(3, 5, 8);
1246 				}
1247 			} else {
1248 				Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_EnemyDamage));
1249 			}
1250 			if (target->GetType() == Game_Battler::Type_Enemy) {
1251 				static_cast<Game_Enemy*>(target)->SetBlinkTimer();
1252 			}
1253 		}
1254 
1255 		std::string msg;
1256 		if (action->IsAbsorbHp()) {
1257 			msg = BattleMessage::GetHpAbsorbedMessage(*action->GetTarget(), *target, -dmg);
1258 		} else {
1259 			if (dmg == 0) {
1260 				msg = BattleMessage::GetUndamagedMessage(*target);
1261 			} else {
1262 				msg = BattleMessage::GetDamagedMessage(*target, -dmg);
1263 			}
1264 		}
1265 
1266 		battle_message_window->Push(msg);
1267 		battle_message_window->ScrollToEnd();
1268 		if (action->IsAbsorbHp()) {
1269 			SetWait(20, 60);
1270 		} else {
1271 			SetWait(20, 40);
1272 		}
1273 
1274 		SetBattleActionSubState(eApply);
1275 		return BattleActionReturn::eContinue;
1276 	}
1277 
1278 	if (battle_action_substate == eApply) {
1279 		// Hp damage is delayed until after the message, so that the target death animation
1280 		// occurs at the right time.
1281 		action->ApplyHpEffect();
1282 
1283 		auto* target = action->GetTarget();
1284 		assert(target);
1285 		if (target->IsDead()) {
1286 			ProcessBattleActionDeath(action);
1287 		}
1288 
1289 		battle_action_dmg_index = battle_message_window->GetLineCount();
1290 
1291 		SetBattleActionSubState(ePreStates);
1292 		return BattleActionReturn::eContinue;
1293 	}
1294 
1295 	if (battle_action_substate == ePreStates) {
1296 		auto* target = action->GetTarget();
1297 		const auto& states = action->GetStateEffects();
1298 		auto& idx = battle_action_substate_index;
1299 		for (;idx < static_cast<int>(states.size()); ++idx) {
1300 			auto& se = states[idx];
1301 			auto* state = lcf::ReaderUtil::GetElement(lcf::Data::states, se.state_id);
1302 			if (!state || se.effect != Game_BattleAlgorithm::StateEffect::HealedByAttack) {
1303 				continue;
1304 			}
1305 			action->ApplyStateEffect(se);
1306 			pending_message = BattleMessage::GetStateRecoveryMessage(*target, *state);
1307 			++battle_action_substate_index;
1308 
1309 			battle_message_window->PopUntil(battle_action_dmg_index);
1310 			battle_message_window->ScrollToEnd();
1311 			SetWait(4,4);
1312 
1313 			SetBattleActionSubState(eStates, false);
1314 			return BattleActionReturn::eContinue;
1315 		}
1316 		SetBattleActionSubState(ePost);
1317 		return BattleActionReturn::eContinue;
1318 	}
1319 
1320 	if (battle_action_substate == eStates) {
1321 		battle_message_window->Push(pending_message);
1322 		battle_message_window->ScrollToEnd();
1323 		SetWait(20, 40);
1324 
1325 		SetBattleActionSubState(ePreStates, false);
1326 		return BattleActionReturn::eContinue;
1327 	}
1328 
1329 	if (battle_action_substate == ePost) {
1330 		SetWait(0, 10);
1331 	}
1332 
1333 	SetBattleActionState(BattleActionState_Params);
1334 	return BattleActionReturn::eContinue;
1335 }
1336 
ProcessBattleActionParamEffects(Game_BattleAlgorithm::AlgorithmBase * action)1337 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionParamEffects(Game_BattleAlgorithm::AlgorithmBase* action) {
1338 	enum SubState {
1339 		ePreHp,
1340 		eHp,
1341 		ePreSp,
1342 		eSp,
1343 		ePreAtk,
1344 		eAtk,
1345 		ePreDef,
1346 		eDef,
1347 		ePreSpi,
1348 		eSpi,
1349 		ePreAgi,
1350 		eAgi,
1351 		eDone
1352 	};
1353 
1354 	const auto next_state = BattleActionState_States;
1355 	auto* source = action->GetSource();
1356 	auto* target = action->GetTarget();
1357 
1358 	// All of the "Pre" states are even numbers, so catch all Pre here.
1359 	if ((battle_action_substate & 1) == 0) {
1360 		pending_message.clear();
1361 
1362 		auto checkNext = [&]() {
1363 			if (pending_message.empty()) {
1364 				SetBattleActionSubState(battle_action_substate + 2);
1365 			}
1366 		};
1367 
1368 		if (battle_action_substate == ePreHp) {
1369 			// Damage is handled by Damage state, so only check healing here.
1370 			if (action->GetAffectedHp() > 0 && !action->IsRevived()) {
1371 				auto hp = action->ApplyHpEffect();
1372 				pending_message = BattleMessage::GetHpRecoveredMessage(*target, hp);
1373 			}
1374 			checkNext();
1375 		}
1376 
1377 		if (battle_action_substate == ePreSp) {
1378 			auto sp = action->ApplySpEffect();
1379 			if (action->IsAbsorbSp()) {
1380 				pending_message = BattleMessage::GetSpAbsorbedMessage(*source, *target, -sp);
1381 			} else {
1382 				if (sp > 0) {
1383 					pending_message = BattleMessage::GetSpRecoveredMessage(*target, sp);
1384 				}
1385 				if (sp < 0) {
1386 					pending_message = BattleMessage::GetSpReduceMessage(*target, -sp);
1387 				}
1388 			}
1389 			checkNext();
1390 		}
1391 
1392 		if (battle_action_substate == ePreAtk) {
1393 			auto atk = action->ApplyAtkEffect();
1394 			if (atk != 0) {
1395 				if (action->IsAbsorbAtk()) {
1396 					pending_message = BattleMessage::GetAtkAbsorbedMessage(*source, *target, -atk);
1397 				} else {
1398 					pending_message = BattleMessage::GetAtkChangeMessage(*target, atk);
1399 				}
1400 			}
1401 			checkNext();
1402 		}
1403 
1404 		if (battle_action_substate == ePreDef) {
1405 			auto def = action->ApplyDefEffect();
1406 			if (def != 0) {
1407 				if (action->IsAbsorbDef()) {
1408 					pending_message = BattleMessage::GetDefAbsorbedMessage(*source, *target, -def);
1409 				} else {
1410 					pending_message = BattleMessage::GetDefChangeMessage(*target, def);
1411 				}
1412 			}
1413 			checkNext();
1414 		}
1415 
1416 		if (battle_action_substate == ePreSpi) {
1417 			auto spi = action->ApplySpiEffect();
1418 			if (spi != 0) {
1419 				if (action->IsAbsorbSpi()) {
1420 					pending_message = BattleMessage::GetSpiAbsorbedMessage(*source, *target, -spi);
1421 				} else {
1422 					pending_message = BattleMessage::GetSpiChangeMessage(*target, spi);
1423 				}
1424 			}
1425 			checkNext();
1426 		}
1427 
1428 		if (battle_action_substate == ePreAgi) {
1429 			auto agi = action->ApplyAgiEffect();
1430 			if (agi != 0) {
1431 				if (action->IsAbsorbAgi()) {
1432 					pending_message = BattleMessage::GetAgiAbsorbedMessage(*source, *target, -agi);
1433 				} else {
1434 					pending_message = BattleMessage::GetAgiChangeMessage(*target, agi);
1435 				}
1436 			}
1437 			checkNext();
1438 		}
1439 
1440 		if (!pending_message.empty()) {
1441 			battle_message_window->PopUntil(battle_action_results_index);
1442 			battle_message_window->ScrollToEnd();
1443 			SetWait(4,4);
1444 
1445 			SetBattleActionSubState(battle_action_substate + 1, false);
1446 			return BattleActionReturn::eContinue;
1447 		}
1448 	}
1449 
1450 	// Use >= here so that the each "pre" stage above can just call
1451 	// checkNext() to increment +2 without worrying about overflowing
1452 	// past eDone.
1453 	if (battle_action_substate >= eDone) {
1454 		SetBattleActionState(next_state);
1455 		return BattleActionReturn::eContinue;
1456 	}
1457 
1458 	// All of the normal states are odd numbers.
1459 	if ((battle_action_substate & 1) != 0) {
1460 		battle_message_window->Push(pending_message);
1461 		battle_message_window->ScrollToEnd();
1462 		SetWait(20, 60);
1463 
1464 		SetBattleActionSubState(battle_action_substate + 1);
1465 		return BattleActionReturn::eContinue;
1466 	}
1467 
1468 	SetBattleActionState(next_state);
1469 	return BattleActionReturn::eContinue;
1470 }
1471 
ProcessBattleActionStateEffects(Game_BattleAlgorithm::AlgorithmBase * action)1472 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionStateEffects(Game_BattleAlgorithm::AlgorithmBase* action) {
1473 	enum SubState {
1474 		eApply,
1475 		ePreWait,
1476 		eMessage,
1477 		eDone
1478 	};
1479 
1480 	const auto next_state = BattleActionState_Attributes;
1481 
1482 	auto* target = action->GetTarget();
1483 
1484 	if (battle_action_substate == eApply) {
1485 		pending_message.clear();
1486 
1487 		const auto was_dead = target->IsDead();
1488 		const auto& states = action->GetStateEffects();
1489 		auto& idx = battle_action_substate_index;
1490 
1491 		if (idx >= static_cast<int>(states.size())) {
1492 			SetBattleActionState(next_state);
1493 			return BattleActionReturn::eContinue;
1494 		}
1495 
1496 		for (;idx < (int)states.size(); ++idx) {
1497 			auto& se = states[idx];
1498 			// Already applied earlier after damage
1499 			if (se.effect == Game_BattleAlgorithm::StateEffect::HealedByAttack) {
1500 				++idx;
1501 				return BattleActionReturn::eContinue;
1502 			}
1503 
1504 			auto* state = lcf::ReaderUtil::GetElement(lcf::Data::states, se.state_id);
1505 			if (!state) {
1506 				continue;
1507 			}
1508 
1509 			action->ApplyStateEffect(se);
1510 			switch (se.effect) {
1511 				case Game_BattleAlgorithm::StateEffect::Inflicted:
1512 					pending_message = BattleMessage::GetStateInflictMessage(*target, *state);
1513 					break;
1514 				case Game_BattleAlgorithm::StateEffect::Healed:
1515 					pending_message = BattleMessage::GetStateRecoveryMessage(*target, *state);
1516 					break;
1517 				case Game_BattleAlgorithm::StateEffect::AlreadyInflicted:
1518 					pending_message = BattleMessage::GetStateAlreadyMessage(*target, *state);
1519 					break;
1520 				default:
1521 					break;
1522 			}
1523 
1524 			if ((!was_dead && target->IsDead()) || !pending_message.empty()) {
1525 				break;
1526 			}
1527 		}
1528 
1529 		battle_message_window->PopUntil(battle_action_results_index);
1530 		battle_message_window->ScrollToEnd();
1531 
1532 		// If we were killed by state
1533 		if (!was_dead && target->IsDead()) {
1534 			ProcessBattleActionDeath(action);
1535 			SetBattleActionState(next_state);
1536 			// FIXES an RPG_RT bug where RPG_RT does an extra SetWait(4,4), SetWait(20,60) on death state infliction
1537 			return BattleActionReturn::eContinue;
1538 		}
1539 		SetBattleActionSubState(ePreWait, false);
1540 		return BattleActionReturn::eContinue;
1541 	}
1542 
1543 	if (battle_action_substate == ePreWait) {
1544 		SetWait(4,4);
1545 		SetBattleActionSubState(eMessage, false);
1546 		return BattleActionReturn::eContinue;
1547 	}
1548 
1549 	if (battle_action_substate == eMessage) {
1550 		battle_message_window->Push(pending_message);
1551 		battle_message_window->ScrollToEnd();
1552 		SetWait(20, 60);
1553 
1554 		// Process the next state
1555 		++battle_action_substate_index;
1556 		SetBattleActionSubState(eApply, false);
1557 		return BattleActionReturn::eContinue;
1558 	}
1559 
1560 	SetBattleActionState(next_state);
1561 	return BattleActionReturn::eContinue;
1562 }
1563 
ProcessBattleActionAttributeEffects(Game_BattleAlgorithm::AlgorithmBase * action)1564 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionAttributeEffects(Game_BattleAlgorithm::AlgorithmBase* action) {
1565 	enum SubState {
1566 		eApply,
1567 		eMessage,
1568 	};
1569 
1570 	const auto next_state = BattleActionState_Finished;
1571 
1572 	// All of the "Pre" states are even numbers, so catch all Pre here.
1573 	if (battle_action_substate == eApply) {
1574 		pending_message.clear();
1575 
1576 		const auto& attrs = action->GetShiftedAttributes();
1577 		auto& idx = battle_action_substate_index;
1578 		if (idx >= static_cast<int>(attrs.size())) {
1579 			SetBattleActionState(next_state);
1580 			return BattleActionReturn::eContinue;
1581 		}
1582 
1583 		for (;idx < (int)attrs.size(); ++idx) {
1584 			auto& ae = attrs[battle_action_substate_index];
1585 			auto shifted = action->ApplyAttributeShiftEffect(ae);
1586 			if (shifted != 0) {
1587 				auto* attr = lcf::ReaderUtil::GetElement(lcf::Data::attributes, ae.attr_id);
1588 				pending_message = BattleMessage::GetAttributeShiftMessage(*action->GetTarget(), shifted, *attr);
1589 				break;
1590 			}
1591 		}
1592 
1593 		battle_message_window->PopUntil(battle_action_results_index);
1594 		battle_message_window->ScrollToEnd();
1595 		SetWait(4,4);
1596 
1597 		SetBattleActionSubState(eMessage, false);
1598 		return BattleActionReturn::eContinue;
1599 	}
1600 
1601 	// All of the normal states are odd numbers.
1602 	if (battle_action_substate == eMessage) {
1603 		battle_message_window->Push(pending_message);
1604 		battle_message_window->ScrollToEnd();
1605 		SetWait(20, 60);
1606 
1607 		++battle_action_substate_index;
1608 		SetBattleActionSubState(eApply, false);
1609 		return BattleActionReturn::eContinue;
1610 	}
1611 
1612 	SetBattleActionState(next_state);
1613 	return BattleActionReturn::eContinue;
1614 }
1615 
ProcessBattleActionDeath(Game_BattleAlgorithm::AlgorithmBase * action)1616 void Scene_Battle_Rpg2k::ProcessBattleActionDeath(Game_BattleAlgorithm::AlgorithmBase* action) {
1617 	auto* target = action->GetTarget();
1618 	assert(target);
1619 
1620 	battle_message_window->Push(BattleMessage::GetDeathMessage(*action->GetTarget()));
1621 	battle_message_window->ScrollToEnd();
1622 	SetWait(36, 60);
1623 
1624 	if (target->GetType() == Game_Battler::Type_Enemy) {
1625 		static_cast<Game_Enemy*>(target)->SetDeathTimer();
1626 		Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_EnemyKill));
1627 	}
1628 }
1629 
ProcessBattleActionFinished(Game_BattleAlgorithm::AlgorithmBase * action)1630 Scene_Battle_Rpg2k::BattleActionReturn Scene_Battle_Rpg2k::ProcessBattleActionFinished(Game_BattleAlgorithm::AlgorithmBase* action) {
1631 	if (action->RepeatNext(true) || action->TargetNext()) {
1632 		// Clear the console for the next target
1633 		battle_message_window->PopUntil(battle_action_start_index);
1634 		battle_message_window->ScrollToEnd();
1635 
1636 		SetBattleActionState(BattleActionState_Execute);
1637 		return BattleActionReturn::eContinue;
1638 	}
1639 
1640 	battle_message_window->Clear();
1641 	action->ProcessPostActionSwitches();
1642 	return BattleActionReturn::eFinished;
1643 }
1644 
SelectNextActor(bool auto_battle)1645 void Scene_Battle_Rpg2k::SelectNextActor(bool auto_battle) {
1646 	std::vector<Game_Actor*> allies = Main_Data::game_party->GetActors();
1647 
1648 	if ((size_t)actor_index == allies.size()) {
1649 		// All actor actions decided, player turn ends
1650 		SetState(State_Battle);
1651 		NextTurn();
1652 
1653 		CreateEnemyActions();
1654 		CreateExecutionOrder();
1655 
1656 		return;
1657 	}
1658 
1659 	active_actor = allies[actor_index];
1660 	status_window->SetIndex(actor_index);
1661 	actor_index++;
1662 
1663 	Game_Battler* random_target = NULL;
1664 
1665 	if (!active_actor->CanAct()) {
1666 		active_actor->SetBattleAlgorithm(std::make_shared<Game_BattleAlgorithm::None>(active_actor));
1667 		battle_actions.push_back(active_actor);
1668 		SelectNextActor(auto_battle);
1669 		return;
1670 	}
1671 
1672 	switch (active_actor->GetSignificantRestriction()) {
1673 		case lcf::rpg::State::Restriction_attack_ally:
1674 			random_target = Main_Data::game_party->GetRandomActiveBattler();
1675 			break;
1676 		case lcf::rpg::State::Restriction_attack_enemy:
1677 			random_target = Main_Data::game_enemyparty->GetRandomActiveBattler();
1678 			break;
1679 		default:
1680 			break;
1681 	}
1682 
1683 	if (random_target) {
1684 		// RPG_RT doesn't support "Attack All" weapons when battler is confused or provoked.
1685 		active_actor->SetBattleAlgorithm(std::make_shared<Game_BattleAlgorithm::Normal>(active_actor, random_target));
1686 		battle_actions.push_back(active_actor);
1687 
1688 		SelectNextActor(auto_battle);
1689 		return;
1690 	}
1691 
1692 	if (auto_battle || active_actor->GetAutoBattle()) {
1693 		this->autobattle_algo->SetAutoBattleAction(*active_actor);
1694 		assert(active_actor->GetBattleAlgorithm() != nullptr);
1695 		battle_actions.push_back(active_actor);
1696 
1697 		SelectNextActor(auto_battle);
1698 		return;
1699 	}
1700 
1701 	SetState(Scene_Battle::State_SelectCommand);
1702 }
1703 
SelectPreviousActor()1704 void Scene_Battle_Rpg2k::SelectPreviousActor() {
1705 	std::vector<Game_Actor*> allies = Main_Data::game_party->GetActors();
1706 
1707 	if (allies[0] == active_actor) {
1708 		SetState(State_SelectOption);
1709 		actor_index = 0;
1710 		return;
1711 	}
1712 
1713 	actor_index--;
1714 	active_actor = allies[actor_index];
1715 
1716 	battle_actions.back()->SetBattleAlgorithm(nullptr);
1717 	battle_actions.pop_back();
1718 
1719 	if (!active_actor->IsControllable()) {
1720 		SelectPreviousActor();
1721 		return;
1722 	}
1723 
1724 	SetState(State_SelectActor);
1725 }
1726 
CreateExecutionOrder()1727 void Scene_Battle_Rpg2k::CreateExecutionOrder() {
1728 	// Define random Agility. Must be done outside of the sort function because of the "strict weak ordering" property, so the sort is consistent
1729 	for (auto battler : battle_actions) {
1730 		int battle_order = battler->GetAgi() + Rand::GetRandomNumber(0, battler->GetAgi() / 4 + 3);
1731 		if (battler->GetBattleAlgorithm()->GetType() == Game_BattleAlgorithm::Type::Normal && battler->HasPreemptiveAttack()) {
1732 			// RPG_RT sets this value
1733 			battle_order += 9999;
1734 		}
1735 		battler->SetBattleOrderAgi(battle_order);
1736 	}
1737 	std::sort(battle_actions.begin(), battle_actions.end(),
1738 			[](Game_Battler* l, Game_Battler* r) {
1739 			return l->GetBattleOrderAgi() > r->GetBattleOrderAgi();
1740 			});
1741 
1742 	for (const auto& battler : battle_actions) {
1743 		if (std::count(battle_actions.begin(), battle_actions.end(), battler) > 1) {
1744 			Output::Warning("CreateExecutionOrder: Battler {} ({}) has multiple battle actions", battler->GetId(), battler->GetName());
1745 			Output::Warning("Please report a bug!");
1746 			break;
1747 		}
1748 	}
1749 }
1750 
CreateEnemyActions()1751 void Scene_Battle_Rpg2k::CreateEnemyActions() {
1752 	if (first_strike) {
1753 		return;
1754 	}
1755 
1756 	for (auto* enemy : Main_Data::game_enemyparty->GetEnemies()) {
1757 		if (!EnemyAi::SetStateRestrictedAction(*enemy)) {
1758 			enemyai_algo->SetEnemyAiAction(*enemy);
1759 		}
1760 		assert(enemy->GetBattleAlgorithm() != nullptr);
1761 		ActionSelectedCallback(enemy);
1762 	}
1763 }
1764 
ActionSelectedCallback(Game_Battler * for_battler)1765 void Scene_Battle_Rpg2k::ActionSelectedCallback(Game_Battler* for_battler) {
1766 	Scene_Battle::ActionSelectedCallback(for_battler);
1767 
1768 	if (for_battler->GetType() == Game_Battler::Type_Ally) {
1769 		SetState(State_SelectActor);
1770 	}
1771 }
1772 
SetWait(int min_wait,int max_wait)1773 void Scene_Battle_Rpg2k::SetWait(int min_wait, int max_wait) {
1774 #if defined(EP_DEBUG_BATTLE2K_MESSAGE) || defined(EP_DEBUG_BATTLE2K_STATE_MACHINE)
1775 	Output::Debug("Battle2k Wait({},{}) frame={}", min_wait, max_wait, Main_Data::game_system->GetFrameCounter());
1776 #endif
1777 	battle_action_wait = max_wait;
1778 	battle_action_min_wait = max_wait - min_wait;
1779 }
1780 
SetWaitForUsage(Game_BattleAlgorithm::Type type,int anim_frames)1781 void Scene_Battle_Rpg2k::SetWaitForUsage(Game_BattleAlgorithm::Type type, int anim_frames) {
1782 	int min_wait = 0;
1783 	int max_wait = 0;
1784 	switch (type) {
1785 		case Game_BattleAlgorithm::Type::Normal:
1786 			min_wait = 20;
1787 			max_wait = 40;
1788 			break;
1789 		case Game_BattleAlgorithm::Type::Escape:
1790 			min_wait = 36;
1791 			max_wait = 60;
1792 			break;
1793 		case Game_BattleAlgorithm::Type::None:
1794 		case Game_BattleAlgorithm::Type::DoNothing:
1795 			min_wait = max_wait = 0;
1796 			break;
1797 		default:
1798 			min_wait = 20;
1799 			max_wait = 60;
1800 			break;
1801 	}
1802 	SetWait(std::max(min_wait, anim_frames), std::max(max_wait, anim_frames));
1803 }
1804 
CheckWait()1805 bool Scene_Battle_Rpg2k::CheckWait() {
1806 	if (battle_action_wait > 0) {
1807 		if (Input::IsPressed(Input::CANCEL)) {
1808 			return false;
1809 		}
1810 		--battle_action_wait;
1811 		if (battle_action_wait > battle_action_min_wait) {
1812 			return false;
1813 		}
1814 		if (!Input::IsPressed(Input::DECISION)
1815 			&& !Input::IsPressed(Input::SHIFT)
1816 			&& battle_action_wait > 0) {
1817 			return false;
1818 		}
1819 		battle_action_wait = 0;
1820 	}
1821 	return true;
1822 }
1823 
PushExperienceGainedMessage(PendingMessage & pm,int exp)1824 void Scene_Battle_Rpg2k::PushExperienceGainedMessage(PendingMessage& pm, int exp) {
1825 	if (Player::IsRPG2kE()) {
1826 		pm.PushLine(
1827 			Utils::ReplacePlaceholders(
1828 				lcf::Data::terms.exp_received,
1829 				Utils::MakeArray('V', 'U'),
1830 				Utils::MakeSvArray(std::to_string(exp), lcf::Data::terms.exp_short)
1831 			) + Player::escape_symbol + "."
1832 		);
1833 	}
1834 	else {
1835 		std::stringstream ss;
1836 		ss << exp << lcf::Data::terms.exp_received << Player::escape_symbol << ".";
1837 		pm.PushLine(ss.str());
1838 	}
1839 }
1840 
PushGoldReceivedMessage(PendingMessage & pm,int money)1841 void Scene_Battle_Rpg2k::PushGoldReceivedMessage(PendingMessage& pm, int money) {
1842 
1843 	if (Player::IsRPG2kE()) {
1844 		pm.PushLine(
1845 			Utils::ReplacePlaceholders(
1846 				lcf::Data::terms.gold_recieved_a,
1847 				Utils::MakeArray('V', 'U'),
1848 				Utils::MakeSvArray(std::to_string(money), lcf::Data::terms.gold)
1849 			) + Player::escape_symbol + "."
1850 		);
1851 	}
1852 	else {
1853 		std::stringstream ss;
1854 		ss << lcf::Data::terms.gold_recieved_a << " " << money << lcf::Data::terms.gold << lcf::Data::terms.gold_recieved_b << Player::escape_symbol << ".";
1855 		pm.PushLine(ss.str());
1856 	}
1857 }
1858 
PushItemRecievedMessages(PendingMessage & pm,std::vector<int> drops)1859 void Scene_Battle_Rpg2k::PushItemRecievedMessages(PendingMessage& pm, std::vector<int> drops) {
1860 	std::stringstream ss;
1861 
1862 	for (std::vector<int>::iterator it = drops.begin(); it != drops.end(); ++it) {
1863 		const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, *it);
1864 		// No Output::Warning needed here, reported later when the item is added
1865 		StringView item_name = item ? StringView(item->name) : StringView("??? BAD ITEM ???");
1866 
1867 		if (Player::IsRPG2kE()) {
1868 			pm.PushLine(
1869 				Utils::ReplacePlaceholders(
1870 					lcf::Data::terms.item_recieved,
1871 					Utils::MakeArray('S'),
1872 					Utils::MakeSvArray(item_name)
1873 				) + Player::escape_symbol + "."
1874 			);
1875 		}
1876 		else {
1877 			ss.str("");
1878 			ss << item_name << lcf::Data::terms.item_recieved << Player::escape_symbol << ".";
1879 			pm.PushLine(ss.str());
1880 		}
1881 	}
1882 }
1883 
CheckBattleEndConditions()1884 bool Scene_Battle_Rpg2k::CheckBattleEndConditions() {
1885 	if (state == State_Defeat || Game_Battle::CheckLose()) {
1886 		if (state != State_Defeat) {
1887 			SetState(State_Defeat);
1888 		}
1889 		return true;
1890 	}
1891 
1892 	if (state == State_Victory || Game_Battle::CheckWin()) {
1893 		if (state != State_Victory) {
1894 			SetState(State_Victory);
1895 		}
1896 		return true;
1897 	}
1898 
1899 	return false;
1900 }
1901 
1902