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