1 #include "test_mock_actor.h"
2 #include "enemyai.h"
3 #include "rand.h"
4 #include "algo.h"
5 #include "game_battlealgorithm.h"
6 #include "game_switches.h"
7 #include "main_data.h"
8 #include "doctest.h"
9 
10 #include <iostream>
11 
MakeEnemy(int id)12 static Game_Enemy MakeEnemy(int id) {
13 	MakeDBEnemy(id, 100, 100, 1, 1, 1, 1);
14 	auto& tp = lcf::Data::troops[0];
15 	tp.members.resize(8);
16 	tp.members[id - 1].enemy_id = id;
17 	Main_Data::game_enemyparty->ResetBattle(1);
18 	auto& enemy = (*Main_Data::game_enemyparty)[id - 1];
19 	return std::move(enemy);
20 }
21 
MakeActor(int id)22 decltype(auto) MakeActor(int id) {
23 	auto actor = Game_Actor(id);
24 	return actor;
25 }
26 
MakeSkill(int id,int type,int scope)27 lcf::rpg::Skill* MakeSkill(int id, int type, int scope) {
28 	auto* s = MakeDBSkill(id, 90, 0, 0, 0, 0);
29 	s->type = type;
30 	s->scope = scope;
31 	return s;
32 }
33 
34 TEST_SUITE_BEGIN("EnemyAi");
35 
testIrregularSkill(bool expect,int type,const Game_Battler & target,bool bugs)36 static void testIrregularSkill(bool expect, int type, const Game_Battler& target, bool bugs) {
37 	REQUIRE_EQ(expect, EnemyAi::IsSkillEffectiveOn(*MakeSkill(1, type, lcf::rpg::Skill::Scope_enemy), target, bugs));
38 	REQUIRE_EQ(expect, EnemyAi::IsSkillEffectiveOn(*MakeSkill(1, type, lcf::rpg::Skill::Scope_enemies), target, bugs));
39 	REQUIRE_EQ(expect, EnemyAi::IsSkillEffectiveOn(*MakeSkill(1, type, lcf::rpg::Skill::Scope_self), target, bugs));
40 	REQUIRE_EQ(expect, EnemyAi::IsSkillEffectiveOn(*MakeSkill(1, type, lcf::rpg::Skill::Scope_ally), target, bugs));
41 	REQUIRE_EQ(expect, EnemyAi::IsSkillEffectiveOn(*MakeSkill(1, type, lcf::rpg::Skill::Scope_party), target, bugs));
42 }
43 
testNormalSkill(bool expect_enemy,bool expect_ally,lcf::rpg::Skill & skill,const Game_Battler & target,bool bugs)44 static void testNormalSkill(bool expect_enemy, bool expect_ally, lcf::rpg::Skill& skill, const Game_Battler& target, bool bugs) {
45 	CAPTURE(bugs);
46 	CAPTURE(target.IsDead());
47 	CAPTURE(target.IsHidden());
48 	CAPTURE(target.HasState(2));
49 	CAPTURE(skill.scope);
50 
51 	skill.scope = lcf::rpg::Skill::Scope_enemy;
52 	REQUIRE_EQ(expect_enemy, EnemyAi::IsSkillEffectiveOn(skill, target, bugs));
53 
54 	skill.scope = lcf::rpg::Skill::Scope_enemies;
55 	REQUIRE_EQ(expect_enemy, EnemyAi::IsSkillEffectiveOn(skill, target, bugs));
56 
57 	skill.scope = lcf::rpg::Skill::Scope_self;
58 	REQUIRE_EQ(expect_ally, EnemyAi::IsSkillEffectiveOn(skill, target, bugs));
59 
60 	skill.scope = lcf::rpg::Skill::Scope_ally;
61 	REQUIRE_EQ(expect_ally, EnemyAi::IsSkillEffectiveOn(skill, target, bugs));
62 
63 	skill.scope = lcf::rpg::Skill::Scope_party;
64 	REQUIRE_EQ(expect_ally, EnemyAi::IsSkillEffectiveOn(skill, target, bugs));
65 }
66 
67 TEST_CASE("IsSkillEffectiveOn") {
68 	const MockActor m;
69 
70 	std::vector<Game_Enemy> targets;
71 	targets.push_back(MakeEnemy(1));
72 	targets.push_back(MakeEnemy(2));
73 	targets.back().Kill();
74 	targets.push_back(MakeEnemy(3));
75 	targets.back().SetHidden(true);
76 	targets.push_back(MakeEnemy(4));
77 	targets.back().Kill();
78 	targets.back().SetHidden(true);
79 	targets.push_back(MakeEnemy(5));
80 	targets.back().AddState(2, true);
81 
82 	SUBCASE("Switch") {
83 		for (auto& t: targets) {
84 			testIrregularSkill(true, lcf::rpg::Skill::Type_switch, t, true);
85 			testIrregularSkill(true, lcf::rpg::Skill::Type_switch, t, false);
86 		}
87 	}
88 
89 	SUBCASE("Escape") {
90 		for (auto& t: targets) {
91 			testIrregularSkill(false, lcf::rpg::Skill::Type_escape, t, true);
92 			testIrregularSkill(false, lcf::rpg::Skill::Type_escape, t, false);
93 		}
94 	}
95 
96 	SUBCASE("Teleport") {
97 		for (auto& t: targets) {
98 			testIrregularSkill(false, lcf::rpg::Skill::Type_teleport, t, true);
99 			testIrregularSkill(false, lcf::rpg::Skill::Type_teleport, t, false);
100 		}
101 	}
102 
103 	SUBCASE("Normal") {
104 		auto* skill = MakeSkill(1, lcf::rpg::Skill::Type_normal, lcf::rpg::Skill::Scope_self);
105 
106 		SUBCASE("None") {
107 			for (auto& t: targets) {
108 				testNormalSkill(t.Exists(), false, *skill, t, true);
109 				testNormalSkill(t.Exists(), false, *skill, t, false);
110 			}
111 		}
112 
113 		SUBCASE("Hp") {
114 			skill->affect_hp = true;
115 			for (auto& t: targets) {
116 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
117 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
118 			}
119 		}
120 
121 		SUBCASE("Sp") {
122 			skill->affect_sp = true;
123 			for (auto& t: targets) {
124 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
125 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
126 			}
127 		}
128 
129 		SUBCASE("Atk") {
130 			skill->affect_attack = true;
131 			for (auto& t: targets) {
132 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
133 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
134 			}
135 		}
136 
137 		SUBCASE("Def") {
138 			skill->affect_defense = true;
139 			for (auto& t: targets) {
140 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
141 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
142 			}
143 		}
144 
145 		SUBCASE("Spi") {
146 			skill->affect_spirit = true;
147 			for (auto& t: targets) {
148 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
149 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
150 			}
151 		}
152 
153 		SUBCASE("Agi") {
154 			skill->affect_agility = true;
155 			for (auto& t: targets) {
156 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
157 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
158 			}
159 		}
160 
161 		SUBCASE("Death") {
162 			skill->state_effects = { true };
163 			for (auto& t: targets) {
164 				testNormalSkill(t.Exists(), !t.Exists(), *skill, t, true);
165 				testNormalSkill(t.Exists(), t.IsDead(), *skill, t, false);
166 			}
167 		}
168 
169 		SUBCASE("ReverseDeath") {
170 			skill->state_effects = { true };
171 			skill->reverse_state_effect = true;
172 			for (auto& t: targets) {
173 				testNormalSkill(t.Exists(), !t.Exists(), *skill, t, true);
174 				testNormalSkill(t.Exists(), !t.IsDead() && t.Exists(), *skill, t, false);
175 			}
176 		}
177 
178 		SUBCASE("State") {
179 			skill->state_effects = { false, true };
180 			for (auto& t: targets) {
181 				testNormalSkill(t.Exists(), t.Exists() && t.HasState(2), *skill, t, true);
182 				testNormalSkill(t.Exists(), t.Exists() && t.HasState(2), *skill, t, false);
183 			}
184 		}
185 
186 		SUBCASE("ReverseState") {
187 			skill->state_effects = { false, true };
188 			skill->reverse_state_effect = true;
189 			for (auto& t: targets) {
190 				testNormalSkill(t.Exists(), t.Exists() && t.HasState(2), *skill, t, true);
191 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
192 			}
193 		}
194 
195 		SUBCASE("Attribute") {
196 			skill->attribute_effects = { true };
197 			for (auto& t: targets) {
198 				testNormalSkill(t.Exists(), false, *skill, t, true);
199 				testNormalSkill(t.Exists(), false, *skill, t, false);
200 			}
201 		}
202 
203 		SUBCASE("AttributeEffect") {
204 			skill->attribute_effects = { true };
205 			skill->affect_attr_defence = true;
206 			for (auto& t: targets) {
207 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, true);
208 				testNormalSkill(t.Exists(), t.Exists(), *skill, t, false);
209 			}
210 		}
211 	}
212 }
213 
214 TEST_CASE("NoActions") {
215 	const MockActor m;
216 
217 	auto enemy = MakeEnemy(1);
218 	EnemyAi::SelectEnemyAiActionRpgRtCompat(enemy, true);
219 	REQUIRE_EQ(enemy.GetBattleAlgorithm(), nullptr);
220 }
221 
222 namespace {
223 struct ActionParams {
224 	int rating;
225 	int kind;
226 	int basic;
227 };
228 }
229 
MakeActions(std::initializer_list<ActionParams> ilist)230 static std::vector<lcf::rpg::EnemyAction> MakeActions(std::initializer_list<ActionParams> ilist) {
231 	std::vector<lcf::rpg::EnemyAction> actions;
232 	for (auto& il: ilist) {
233 		actions.push_back({});
234 		actions.back().rating = il.rating;
235 		actions.back().kind = il.kind;
236 		actions.back().basic = il.basic;
237 	}
238 	return actions;
239 }
240 
testActionType(int start,int end,Game_BattleAlgorithm::Type type_compat,Game_BattleAlgorithm::Type type_improved,Game_Enemy & source)241 static void testActionType(int start, int end, Game_BattleAlgorithm::Type type_compat, Game_BattleAlgorithm::Type type_improved, Game_Enemy& source) {
242 	for (int rng = start; rng < end; ++rng) {
243 		CAPTURE(rng);
244 		bool bugs = true;
245 		CAPTURE(bugs);
246 
247 		Rand::LockGuard lk(rng);
248 		bugs = true;
249 		source.SetBattleAlgorithm(nullptr);
250 		EnemyAi::SelectEnemyAiActionRpgRtCompat(source, bugs);
251 		REQUIRE(source.GetBattleAlgorithm());
252 		REQUIRE_EQ(static_cast<int>(type_compat), static_cast<int>(source.GetBattleAlgorithm()->GetType()));
253 
254 		bugs = false;
255 		source.SetBattleAlgorithm(nullptr);
256 		EnemyAi::SelectEnemyAiActionRpgRtCompat(source, bugs);
257 		REQUIRE(source.GetBattleAlgorithm());
258 		REQUIRE_EQ(static_cast<int>(type_improved), static_cast<int>(source.GetBattleAlgorithm()->GetType()));
259 	}
260 }
261 
testActionNullptr(int start,int end,Game_Enemy & source)262 static void testActionNullptr(int start, int end, Game_Enemy& source) {
263 	for (int rng = start; rng < end; ++rng) {
264 		CAPTURE(rng);
265 		bool bugs = true;
266 		CAPTURE(bugs);
267 
268 		Rand::LockGuard lk(rng);
269 		bugs = true;
270 		source.SetBattleAlgorithm(nullptr);
271 		EnemyAi::SelectEnemyAiActionRpgRtCompat(source, bugs);
272 		REQUIRE_FALSE(source.GetBattleAlgorithm());
273 
274 		bugs = false;
275 		source.SetBattleAlgorithm(nullptr);
276 		EnemyAi::SelectEnemyAiActionRpgRtCompat(source, bugs);
277 		REQUIRE_FALSE(source.GetBattleAlgorithm());
278 	}
279 }
280 
281 TEST_CASE("Ratings") {
282 	const MockActor m;
283 
284 	auto enemy = MakeEnemy(1);
285 	auto& actions = lcf::Data::enemies[0].actions;
286 
287 	SUBCASE("40/50") {
288 		actions = MakeActions({{40, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
289 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
290 	}
291 
292 	SUBCASE("50/40") {
293 		actions = MakeActions({{50, 0, lcf::rpg::EnemyAction::Basic_attack }, { 40, 0, lcf::rpg::EnemyAction::Basic_defense }});
294 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
295 	}
296 
297 	SUBCASE("41/50") {
298 		actions = MakeActions({{41, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
299 		testActionType(0, 1, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
300 		testActionType(1, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
301 	}
302 
303 	SUBCASE("45/50") {
304 		actions = MakeActions({{45, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
305 		testActionType(0, 5, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
306 		testActionType(5, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
307 	}
308 
309 	SUBCASE("49/50") {
310 		actions = MakeActions({{49, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
311 		testActionType(0, 9, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
312 		testActionType(9, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
313 	}
314 
315 	SUBCASE("50/50") {
316 		actions = MakeActions({{50, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
317 		testActionType(0, 10, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
318 		testActionType(10, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
319 	}
320 
321 	SUBCASE("50/30/50") {
322 		actions = MakeActions({{50, 0, lcf::rpg::EnemyAction::Basic_attack }, { 30, 0, lcf::rpg::EnemyAction::Basic_defense }, { 50, 0, lcf::rpg::EnemyAction::Basic_observe }});
323 		testActionType(0, 10, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
324 		testActionType(10, 100, Game_BattleAlgorithm::Type::Observe, Game_BattleAlgorithm::Type::Observe, enemy);
325 	}
326 }
327 
328 TEST_CASE("SkillEffectiveButNotUsable") {
329 	const MockActor m;
330 
331 	auto enemy = MakeEnemy(1);
332 	auto& actions = lcf::Data::enemies[0].actions;
333 	auto* skill = MakeSkill(1, lcf::rpg::Skill::Type_normal, lcf::rpg::Skill::Scope_self);
334 	skill->affect_hp = true;
335 	skill->sp_cost = 10;
336 
337 	actions = MakeActions({{50, lcf::rpg::EnemyAction::Kind_skill, 0}, { 50, 0, lcf::rpg::EnemyAction::Basic_attack }});
338 	actions.front().skill_id = 1;
339 
340 	REQUIRE(EnemyAi::IsSkillEffectiveOn(*skill, enemy, true));
341 
342 	SUBCASE("HasSp") {
343 		enemy.SetSp(100);
344 		REQUIRE_EQ(100, enemy.GetSp());
345 		REQUIRE(enemy.IsSkillUsable(1));
346 		testActionType(0, 10, Game_BattleAlgorithm::Type::Skill, Game_BattleAlgorithm::Type::Skill, enemy);
347 		testActionType(10, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
348 	}
349 
350 	SUBCASE("NoSp") {
351 		enemy.SetSp(0);
352 		REQUIRE_FALSE(enemy.IsSkillUsable(1));
353 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
354 	}
355 }
356 
357 TEST_CASE("SkillUsableButNotEffective") {
358 	const MockActor m;
359 
360 	auto enemy = MakeEnemy(1);
361 
362 	auto& actions = lcf::Data::enemies[0].actions;
363 	auto* skill = MakeSkill(1, lcf::rpg::Skill::Type_normal, lcf::rpg::Skill::Scope_self);
364 	skill->sp_cost = 0;
365 	skill->state_effects = { false, true };
366 	lcf::Data::states[1].type = lcf::rpg::State::Persistence_persists;
367 
368 	REQUIRE(enemy.IsSkillUsable(1));
369 	REQUIRE_FALSE(EnemyAi::IsSkillEffectiveOn(*skill, enemy, true));
370 
371 	SUBCASE("50/50") {
372 		actions = MakeActions({{50, lcf::rpg::EnemyAction::Kind_skill, 0}, { 50, 0, lcf::rpg::EnemyAction::Basic_attack }});
373 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
374 	}
375 
376 	SUBCASE("70/50") {
377 		// If the only valid action is a not effective skill, it's still chosen, but then rejected.
378 		actions = MakeActions({{70, lcf::rpg::EnemyAction::Kind_skill, 0}, { 50, 0, lcf::rpg::EnemyAction::Basic_attack }});
379 		testActionNullptr(0, 100, enemy);
380 	}
381 }
382 
383 TEST_CASE("ConditionSwitch") {
384 	const MockActor m;
385 
386 	auto enemy = MakeEnemy(1);
387 	auto& actions = lcf::Data::enemies[0].actions;
388 
389 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
390 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_switch;
391 	actions.front().switch_id = 1;
392 
393 	SUBCASE("SwOff") {
394 		Main_Data::game_switches->Set(1, false);
395 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
396 	}
397 
398 	SUBCASE("SwOn") {
399 		Main_Data::game_switches->Set(1, true);
400 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
401 	}
402 }
403 
404 TEST_CASE("ConditionTurns") {
405 	const MockActor m;
406 
407 	auto enemy = MakeEnemy(1);
408 	auto& actions = lcf::Data::enemies[0].actions;
409 
410 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
411 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_turn;
412 
413 	Main_Data::game_party->ResetTurns();
414 
415 	SUBCASE("0/0") {
416 		actions.front().condition_param2 = 0;
417 		actions.front().condition_param1 = 0;
418 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
419 	}
420 
421 	SUBCASE("1/1") {
422 		actions.front().condition_param2 = 1;
423 		actions.front().condition_param1 = 1;
424 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
425 	}
426 }
427 
428 TEST_CASE("MonsterParty") {
429 	const MockActor m;
430 
431 	auto enemy = MakeEnemy(1);
432 	auto& actions = lcf::Data::enemies[0].actions;
433 
434 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
435 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_actors;
436 
437 	std::vector<Game_Battler*> battlers;
438 	Main_Data::game_enemyparty->GetActiveBattlers(battlers);
439 	REQUIRE_EQ(8, battlers.size());
440 
441 	SUBCASE("1/1") {
442 		actions.front().condition_param1 = 1;
443 		actions.front().condition_param2 = 1;
444 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
445 	}
446 
447 	SUBCASE("1/8") {
448 		actions.front().condition_param1 = 1;
449 		actions.front().condition_param2 = 8;
450 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
451 	}
452 
453 	SUBCASE("1/7") {
454 		actions.front().condition_param1 = 1;
455 		actions.front().condition_param2 = 7;
456 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
457 	}
458 
459 	SUBCASE("9/10") {
460 		actions.front().condition_param1 = 9;
461 		actions.front().condition_param2 = 10;
462 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
463 	}
464 }
465 
466 TEST_CASE("ConditionHp") {
467 	const MockActor m;
468 
469 	auto enemy = MakeEnemy(1);
470 	auto& actions = lcf::Data::enemies[0].actions;
471 
472 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
473 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_hp;
474 
475 	REQUIRE_EQ(100, enemy.GetMaxHp());
476 	enemy.SetHp(50);
477 	REQUIRE_EQ(50, enemy.GetHp());
478 
479 	SUBCASE("0/100") {
480 		actions.front().condition_param1 = 0;
481 		actions.front().condition_param2 = 100;
482 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
483 	}
484 
485 	SUBCASE("50/50") {
486 		actions.front().condition_param1 = 50;
487 		actions.front().condition_param2 = 50;
488 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
489 	}
490 
491 	SUBCASE("51/100") {
492 		actions.front().condition_param1 = 51;
493 		actions.front().condition_param2 = 100;
494 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
495 	}
496 
497 	SUBCASE("0/49") {
498 		actions.front().condition_param1 = 0;
499 		actions.front().condition_param2 = 49;
500 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
501 	}
502 }
503 
504 TEST_CASE("ConditionSp") {
505 	const MockActor m;
506 
507 	auto enemy = MakeEnemy(1);
508 	auto& actions = lcf::Data::enemies[0].actions;
509 
510 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
511 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_sp;
512 
513 	REQUIRE_EQ(100, enemy.GetMaxSp());
514 	enemy.SetSp(50);
515 	REQUIRE_EQ(50, enemy.GetSp());
516 
517 	SUBCASE("0/100") {
518 		actions.front().condition_param1 = 0;
519 		actions.front().condition_param2 = 100;
520 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
521 	}
522 
523 	SUBCASE("50/50") {
524 		actions.front().condition_param1 = 50;
525 		actions.front().condition_param2 = 50;
526 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
527 	}
528 
529 	SUBCASE("51/100") {
530 		actions.front().condition_param1 = 51;
531 		actions.front().condition_param2 = 100;
532 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
533 	}
534 
535 	SUBCASE("0/49") {
536 		actions.front().condition_param1 = 0;
537 		actions.front().condition_param2 = 49;
538 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
539 	}
540 }
541 
542 TEST_CASE("ConditionLevel") {
543 	const MockActor m;
544 
545 	auto enemy = MakeEnemy(1);
546 	auto& actions = lcf::Data::enemies[0].actions;
547 
548 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
549 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_party_lvl;
550 
551 	Main_Data::game_party->AddActor(1);
552 	Main_Data::game_actors->GetActor(1)->SetLevel(20);
553 	Main_Data::game_party->AddActor(2);
554 	Main_Data::game_actors->GetActor(2)->SetLevel(10);
555 
556 	REQUIRE_EQ(15, Main_Data::game_party->GetAverageLevel());
557 
558 	SUBCASE("0/50") {
559 		actions.front().condition_param1 = 0;
560 		actions.front().condition_param2 = 50;
561 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
562 	}
563 
564 	SUBCASE("15/15") {
565 		actions.front().condition_param1 = 15;
566 		actions.front().condition_param2 = 15;
567 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
568 	}
569 
570 	SUBCASE("16/50") {
571 		actions.front().condition_param1 = 16;
572 		actions.front().condition_param2 = 50;
573 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
574 	}
575 
576 	SUBCASE("0/14") {
577 		actions.front().condition_param1 = 0;
578 		actions.front().condition_param2 = 14;
579 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
580 	}
581 }
582 
583 TEST_CASE("ConditionFatigue") {
584 	const MockActor m;
585 
586 	auto enemy = MakeEnemy(1);
587 	auto& actions = lcf::Data::enemies[0].actions;
588 
589 	actions = MakeActions({{90, 0, lcf::rpg::EnemyAction::Basic_attack }, { 50, 0, lcf::rpg::EnemyAction::Basic_defense }});
590 	actions.front().condition_type = lcf::rpg::EnemyAction::ConditionType_party_fatigue;
591 
592 	Main_Data::game_party->AddActor(1);
593 	Main_Data::game_actors->GetActor(1)->SetBaseMaxHp(100);
594 	Main_Data::game_actors->GetActor(1)->SetHp(50);
595 	Main_Data::game_actors->GetActor(1)->SetBaseMaxSp(0);
596 	Main_Data::game_actors->GetActor(1)->SetSp(0);
597 	Main_Data::game_party->AddActor(2);
598 	Main_Data::game_actors->GetActor(2)->SetBaseMaxHp(100);
599 	Main_Data::game_actors->GetActor(2)->SetHp(80);
600 	Main_Data::game_actors->GetActor(2)->SetBaseMaxSp(0);
601 	Main_Data::game_actors->GetActor(2)->SetSp(0);
602 
603 	REQUIRE_EQ(57, Main_Data::game_party->GetFatigue());
604 
605 	SUBCASE("0/100") {
606 		actions.front().condition_param1 = 0;
607 		actions.front().condition_param2 = 100;
608 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
609 	}
610 
611 	SUBCASE("57/57") {
612 		actions.front().condition_param1 = 57;
613 		actions.front().condition_param2 = 57;
614 		testActionType(0, 100, Game_BattleAlgorithm::Type::Normal, Game_BattleAlgorithm::Type::Normal, enemy);
615 	}
616 
617 	SUBCASE("58/100") {
618 		actions.front().condition_param1 = 58;
619 		actions.front().condition_param2 = 100;
620 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
621 	}
622 
623 	SUBCASE("0/56") {
624 		actions.front().condition_param1 = 0;
625 		actions.front().condition_param2 = 56;
626 		testActionType(0, 100, Game_BattleAlgorithm::Type::Defend, Game_BattleAlgorithm::Type::Defend, enemy);
627 	}
628 }
629 
630 TEST_CASE("SetStateRestrictedAction") {
631 	const MockActor m;
632 
633 	auto enemy = MakeEnemy(1);
634 	Main_Data::game_party->AddActor(1);
635 	lcf::Data::states[1].restriction = lcf::rpg::State::Restriction_do_nothing;
636 	lcf::Data::states[2].restriction = lcf::rpg::State::Restriction_attack_ally;
637 	lcf::Data::states[3].restriction = lcf::rpg::State::Restriction_attack_enemy;
638 
639 	REQUIRE_EQ(nullptr, enemy.GetBattleAlgorithm());
640 
641 	SUBCASE("None") {
642 		REQUIRE_FALSE(EnemyAi::SetStateRestrictedAction(enemy));
643 		REQUIRE_FALSE(enemy.GetBattleAlgorithm());
644 	}
645 
646 	SUBCASE("Charged") {
647 		enemy.SetCharged(true);
648 		REQUIRE(EnemyAi::SetStateRestrictedAction(enemy));
649 		REQUIRE(enemy.GetBattleAlgorithm());
650 		REQUIRE_EQ(static_cast<int>(Game_BattleAlgorithm::Type::Normal), static_cast<int>(enemy.GetBattleAlgorithm()->GetType()));
651 	}
652 
653 	SUBCASE("NoAct") {
654 		enemy.AddState(2, true);
655 		REQUIRE(EnemyAi::SetStateRestrictedAction(enemy));
656 		REQUIRE(enemy.GetBattleAlgorithm());
657 		REQUIRE_EQ(static_cast<int>(Game_BattleAlgorithm::Type::None), static_cast<int>(enemy.GetBattleAlgorithm()->GetType()));
658 	}
659 
660 	SUBCASE("Confuse") {
661 		enemy.AddState(3, true);
662 		REQUIRE(EnemyAi::SetStateRestrictedAction(enemy));
663 		REQUIRE(enemy.GetBattleAlgorithm());
664 		REQUIRE_EQ(static_cast<int>(Game_BattleAlgorithm::Type::Normal), static_cast<int>(enemy.GetBattleAlgorithm()->GetType()));
665 		enemy.GetBattleAlgorithm()->Start();
666 		REQUIRE_EQ(static_cast<int>(Game_Battler::Type_Enemy), static_cast<int>(enemy.GetBattleAlgorithm()->GetTarget()->GetType()));
667 	}
668 
669 	SUBCASE("Provoke") {
670 		enemy.AddState(4, true);
671 		REQUIRE(EnemyAi::SetStateRestrictedAction(enemy));
672 		REQUIRE(enemy.GetBattleAlgorithm());
673 		REQUIRE_EQ(static_cast<int>(Game_BattleAlgorithm::Type::Normal), static_cast<int>(enemy.GetBattleAlgorithm()->GetType()));
674 		enemy.GetBattleAlgorithm()->Start();
675 		REQUIRE_EQ(static_cast<int>(Game_Battler::Type_Ally), static_cast<int>(enemy.GetBattleAlgorithm()->GetTarget()->GetType()));
676 	}
677 }
678 
679 TEST_SUITE_END();
680