1 #include "test_mock_actor.h"
2 #include "doctest.h"
3
nullDBEnemy(lcf::rpg::Enemy &)4 static void nullDBEnemy(lcf::rpg::Enemy&) {}
5
6 template <typename F = decltype(&nullDBEnemy)>
MakeEnemy(int id,int hp,int sp,int atk,int def,int spi,int agi,const F & f=& nullDBEnemy)7 static Game_Enemy& MakeEnemy(int id, int hp, int sp, int atk, int def, int spi, int agi, const F& f = &nullDBEnemy) {
8 auto& tp = lcf::Data::troops[0];
9 tp.members.resize(1);
10 tp.members[0].enemy_id = id;
11 auto* dbe = MakeDBEnemy(id, hp, sp, atk, def, spi, agi);
12 f(*dbe);
13 Main_Data::game_enemyparty->ResetBattle(1);
14 auto& enemy = (*Main_Data::game_enemyparty)[0];
15 return enemy;
16 }
17
18 TEST_SUITE_BEGIN("Game_Enemy");
19
20 TEST_CASE("Default") {
21 const MockActor m;
22
23 auto& e = MakeEnemy(1, 100, 10, 11, 12, 13, 14);
24 const auto& ce = e;
25
26 REQUIRE_EQ(e.GetId(), 1);
27 REQUIRE_EQ(e.GetType(), Game_Battler::Type_Enemy);
28
29 REQUIRE(e.GetStates().empty());
30 REQUIRE(ce.GetStates().empty());
31
32 REQUIRE_EQ(e.GetOriginalPosition(), Point{});
33
34 REQUIRE(e.GetName().empty());
35 REQUIRE(e.GetSpriteName().empty());
36
37 REQUIRE_EQ(e.GetBaseMaxHp(), 100);
38 REQUIRE_EQ(e.GetBaseMaxSp(), 10);
39 REQUIRE_EQ(e.GetHp(), 100);
40 REQUIRE_EQ(e.GetSp(), 10);
41 REQUIRE_EQ(e.GetBaseAtk(), 11);
42 REQUIRE_EQ(e.GetBaseDef(), 12);
43 REQUIRE_EQ(e.GetBaseSpi(), 13);
44 REQUIRE_EQ(e.GetBaseAgi(), 14);
45
46 REQUIRE_EQ(e.GetHue(), 0);
47
48
49 REQUIRE_EQ(e.GetBattleAnimationId(), 0);
50 REQUIRE_EQ(e.GetFlyingOffset(), 0);
51 REQUIRE_EQ(e.IsTransparent(), 0);
52 REQUIRE(e.IsInParty());
53 }
54
testLimits(int hp,int base,int battle)55 static void testLimits(int hp, int base, int battle) {
56 auto& enemy = MakeEnemy(1, 100, 10, 11, 12, 13, 14);
57
58 REQUIRE_EQ(enemy.MaxHpValue(), hp);
59 REQUIRE_EQ(enemy.MaxStatBaseValue(), base);
60 REQUIRE_EQ(enemy.MaxStatBattleValue(), battle);
61
62 SUBCASE("up") {
63 enemy.SetAtkModifier(999999);
64 enemy.SetDefModifier(999999);
65 enemy.SetSpiModifier(999999);
66 enemy.SetAgiModifier(999999);
67 REQUIRE_EQ(enemy.GetAtk(), battle);
68 REQUIRE_EQ(enemy.GetDef(), battle);
69 REQUIRE_EQ(enemy.GetSpi(), battle);
70 REQUIRE_EQ(enemy.GetAgi(), battle);
71 }
72
73 SUBCASE("down") {
74 enemy.SetAtkModifier(-999999);
75 enemy.SetDefModifier(-999999);
76 enemy.SetSpiModifier(-999999);
77 enemy.SetAgiModifier(-999999);
78 REQUIRE_EQ(enemy.GetAtk(), 1);
79 REQUIRE_EQ(enemy.GetDef(), 1);
80 REQUIRE_EQ(enemy.GetSpi(), 1);
81 REQUIRE_EQ(enemy.GetAgi(), 1);
82 }
83 }
84
85 TEST_CASE("Limits") {
86 SUBCASE("2k") {
87 const MockActor m(Player::EngineRpg2k);
88
89 testLimits(9999, 999, 9999);
90 }
91 SUBCASE("2k3") {
92 const MockActor m(Player::EngineRpg2k3);
93
94 testLimits(99999, 999, 9999);
95 }
96 }
97
MakeEnemyHit(bool miss)98 static decltype(auto) MakeEnemyHit(bool miss) {
99 return MakeEnemy(1, 1, 1, 1, 1, 1, 1, [&](auto& e) { e.miss = miss; });
100 }
101
102 TEST_CASE("HitRate") {
103 const MockActor m;
104
105 SUBCASE("default") {
106 auto& e = MakeEnemyHit(false);
107 REQUIRE_EQ(e.GetHitChance(), 90);
108 }
109
110 SUBCASE("miss") {
111 auto& e = MakeEnemyHit(true);
112 REQUIRE_EQ(e.GetHitChance(), 70);
113 }
114 }
115
MakeEnemyCrit(bool crit,int rate)116 static decltype(auto) MakeEnemyCrit(bool crit, int rate) {
117 return MakeEnemy(1, 1, 1, 1, 1, 1, 1, [&](auto& e) { e.critical_hit = crit; e.critical_hit_chance = rate; });
118 }
119
120 TEST_CASE("CritRate") {
121 const MockActor m;
122
123 SUBCASE("disable_0") {
124 auto& e = MakeEnemyCrit(false, 0);
125 REQUIRE_EQ(e.GetCriticalHitChance(), 0.0f);
126 }
127
128 SUBCASE("disable_1") {
129 auto& e = MakeEnemyCrit(false, 1);
130 REQUIRE_EQ(e.GetCriticalHitChance(), 0.0f);
131 }
132
133 SUBCASE("disable_30") {
134 auto& e = MakeEnemyCrit(false, 30);
135 REQUIRE_EQ(e.GetCriticalHitChance(), 0.0f);
136 }
137
138 SUBCASE("enable_0") {
139 auto& e = MakeEnemyCrit(true, 0);
140 REQUIRE_EQ(e.GetCriticalHitChance(), std::numeric_limits<float>::infinity());
141 }
142
143 SUBCASE("enable_1") {
144 auto& e = MakeEnemyCrit(true, 1);
145 REQUIRE_EQ(e.GetCriticalHitChance(), 1.0f);
146 }
147
148 SUBCASE("enable_30") {
149 auto& e = MakeEnemyCrit(true, 30);
150 REQUIRE_EQ(e.GetCriticalHitChance(), doctest::Approx(0.03333f));
151 }
152 }
153
MakeEnemyReward(int exp,int gold,int drop_id,int drop_prob)154 static decltype(auto) MakeEnemyReward(int exp, int gold, int drop_id, int drop_prob) {
155 return MakeEnemy(1, 1, 1, 1, 1, 1, 1, [&](auto& e) {
156 e.exp = exp;
157 e.gold = gold;
158 e.drop_id = drop_id;
159 e.drop_prob = drop_prob;
160 });
161 }
162
163 TEST_CASE("RewardExp") {
164 const MockActor m;
165
166 SUBCASE("0") {
167 auto& e = MakeEnemyReward(0, 0, 0, 0);
168 REQUIRE_EQ(e.GetExp(), 0);
169 }
170
171 SUBCASE("55") {
172 auto& e = MakeEnemyReward(55, 0, 0, 0);
173 REQUIRE_EQ(e.GetExp(), 55);
174 }
175 }
176
177 TEST_CASE("RewardGold") {
178 const MockActor m;
179
180 SUBCASE("0") {
181 auto& e = MakeEnemyReward(0, 0, 0, 0);
182 REQUIRE_EQ(e.GetMoney(), 0);
183 }
184
185 SUBCASE("55") {
186 auto& e = MakeEnemyReward(0, 55, 0, 0);
187 REQUIRE_EQ(e.GetMoney(), 55);
188 }
189 }
190
191 TEST_CASE("RewardItem") {
192 const MockActor m;
193
194 SUBCASE("0_0") {
195 auto& e = MakeEnemyReward(0, 0, 0, 0);
196 REQUIRE_EQ(e.GetDropId(), 0);
197 REQUIRE_EQ(e.GetDropProbability(), 0);
198 }
199
200 SUBCASE("0_55") {
201 auto& e = MakeEnemyReward(0, 0, 0, 55);
202 REQUIRE_EQ(e.GetDropId(), 0);
203 REQUIRE_EQ(e.GetDropProbability(), 55);
204 }
205
206 SUBCASE("1_0") {
207 auto& e = MakeEnemyReward(0, 0, 1, 0);
208 REQUIRE_EQ(e.GetDropId(), 1);
209 REQUIRE_EQ(e.GetDropProbability(), 0);
210 }
211
212 SUBCASE("1_55") {
213 auto& e = MakeEnemyReward(0, 0, 1, 55);
214 REQUIRE_EQ(e.GetDropId(), 1);
215 REQUIRE_EQ(e.GetDropProbability(), 55);
216 }
217 }
218
MakeEnemyAttribute(int id,int attr_id,int rank)219 static Game_Enemy& MakeEnemyAttribute(int id, int attr_id, int rank) {
220 return MakeEnemy(id, 1, 1, 1, 1, 1, 1, [&](auto&) { SetDBEnemyAttribute(id, attr_id, rank); } );
221 }
222
223 TEST_CASE("Attribute") {
224 const MockActor m;
225
226 SUBCASE("0") {
227 const auto& enemy = MakeEnemyAttribute(1, 1, 0);
228 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 0);
229 REQUIRE_EQ(enemy.GetAttributeRate(1), 0);
230 }
231
232 SUBCASE("1") {
233 const auto& enemy = MakeEnemyAttribute(1, 1, 1);
234 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 1);
235 REQUIRE_EQ(enemy.GetAttributeRate(1), 1);
236 }
237
238 SUBCASE("2") {
239 const auto& enemy = MakeEnemyAttribute(1, 1, 2);
240 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 2);
241 REQUIRE_EQ(enemy.GetAttributeRate(1), 2);
242 }
243 }
244
245 TEST_CASE("BadAttribute") {
246 const MockActor m;
247 MakeDBAttribute(1, lcf::rpg::Attribute::Type_physical, -100, 200, 100, 50, 0);
248
249 SUBCASE("0") {
250 const auto& enemy = MakeEnemyAttribute(1, 1, 0);
251 REQUIRE_EQ(enemy.GetBaseAttributeRate(0), 2);
252 REQUIRE_EQ(enemy.GetAttributeRate(0), 2);
253 }
254
255 SUBCASE("INT_MAX") {
256 const auto& enemy = MakeEnemyAttribute(1, 1, 0);
257 REQUIRE_EQ(enemy.GetBaseAttributeRate(INT_MAX), 2);
258 REQUIRE_EQ(enemy.GetAttributeRate(INT_MAX), 2);
259 }
260 }
261
262 TEST_CASE("AttributeShift") {
263 const MockActor m;
264
265 SUBCASE("normal") {
266 auto& enemy = MakeEnemyAttribute(1, 1, 2);
267 REQUIRE_EQ(enemy.GetAttributeRateShift(1), 0);
268 REQUIRE(enemy.CanShiftAttributeRate(1, 1));
269 REQUIRE_EQ(1, enemy.CanShiftAttributeRate(1, 2));
270 REQUIRE(enemy.CanShiftAttributeRate(1, -1));
271 REQUIRE_EQ(-1, enemy.CanShiftAttributeRate(1, -2));
272
273 REQUIRE_EQ(enemy.GetAttributeRateShift(1), 0);
274 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 2);
275 REQUIRE_EQ(enemy.GetAttributeRate(1), 2);
276
277 enemy.ShiftAttributeRate(1, 1);
278 REQUIRE_EQ(enemy.GetAttributeRateShift(1), 1);
279 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 2);
280 REQUIRE_EQ(enemy.GetAttributeRate(1), 3);
281
282 enemy.ShiftAttributeRate(1, -1);
283 REQUIRE_EQ(enemy.GetAttributeRateShift(1), 0);
284 REQUIRE_EQ(enemy.GetAttributeRate(1), 2);
285
286 enemy.ShiftAttributeRate(1, -1);
287 REQUIRE_EQ(enemy.GetAttributeRateShift(1), -1);
288 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 2);
289 REQUIRE_EQ(enemy.GetAttributeRate(1), 1);
290
291 enemy.ShiftAttributeRate(1, 100);
292 REQUIRE_EQ(enemy.GetAttributeRateShift(1), 1);
293 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 2);
294 REQUIRE_EQ(enemy.GetAttributeRate(1), 3);
295
296 enemy.ShiftAttributeRate(1, -100);
297 REQUIRE_EQ(enemy.GetAttributeRateShift(1), -1);
298 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 2);
299 REQUIRE_EQ(enemy.GetAttributeRate(1), 1);
300 }
301
302 SUBCASE("overflow") {
303 auto& enemy = MakeEnemyAttribute(1, 1, 4);
304 enemy.ShiftAttributeRate(1, 1);
305 REQUIRE_EQ(enemy.GetAttributeRateShift(1), 1);
306 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 4);
307 REQUIRE_EQ(enemy.GetAttributeRate(1), 4);
308 }
309
310 SUBCASE("underflow") {
311 auto& enemy = MakeEnemyAttribute(1, 1, 0);
312 enemy.ShiftAttributeRate(1, -1);
313 REQUIRE_EQ(enemy.GetAttributeRateShift(1), -1);
314 REQUIRE_EQ(enemy.GetBaseAttributeRate(1), 0);
315 REQUIRE_EQ(enemy.GetAttributeRate(1), 0);
316 }
317 }
318
319 TEST_CASE("AttributeShiftInvalid") {
320 const MockActor m;
321 auto& enemy = MakeEnemyAttribute(1, 1, 100);
322
323 REQUIRE_EQ(enemy.GetAttributeRateShift(0), 0);
324 REQUIRE_EQ(enemy.GetAttributeRateShift(INT_MAX), 0);
325 REQUIRE_FALSE(enemy.CanShiftAttributeRate(0, 1));
326 REQUIRE_FALSE(enemy.CanShiftAttributeRate(0, -1));
327 REQUIRE_FALSE(enemy.CanShiftAttributeRate(INT_MAX, 1));
328 REQUIRE_FALSE(enemy.CanShiftAttributeRate(INT_MAX, -1));
329
330 enemy.ShiftAttributeRate(0, 1);
331 enemy.ShiftAttributeRate(INT_MAX, 1);
332
333 REQUIRE_EQ(enemy.GetAttributeRateShift(0), 0);
334 REQUIRE_EQ(enemy.GetAttributeRateShift(INT_MAX), 0);
335 REQUIRE_FALSE(enemy.CanShiftAttributeRate(0, 1));
336 REQUIRE_FALSE(enemy.CanShiftAttributeRate(0, -1));
337 REQUIRE_FALSE(enemy.CanShiftAttributeRate(INT_MAX, 1));
338 REQUIRE_FALSE(enemy.CanShiftAttributeRate(INT_MAX, -1));
339 }
340
341 TEST_CASE("ChangeHp") {
342 const MockActor m;
343
344 auto& enemy = MakeEnemy(1, 500, 500, 500, 500, 500, 500);
345
346 REQUIRE_EQ(enemy.GetHp(), 500);
347
348 SUBCASE("dmg") {
349 REQUIRE_EQ(enemy.ChangeHp(-9999, true), -500);
350 REQUIRE_EQ(enemy.GetHp(), 0);
351 REQUIRE(enemy.IsDead());
352
353 REQUIRE_EQ(enemy.ChangeHp(9999, true), 0);
354 }
355
356 SUBCASE("kill") {
357 enemy.Kill();
358 REQUIRE_EQ(enemy.GetHp(), 0);
359 REQUIRE(enemy.IsDead());
360
361 REQUIRE_EQ(enemy.ChangeHp(9999, true), 0);
362 }
363
364 SUBCASE("dmg_nokill") {
365 REQUIRE_EQ(enemy.ChangeHp(-9999, false), -499);
366 REQUIRE_EQ(enemy.GetHp(), 1);
367 REQUIRE_FALSE(enemy.IsDead());
368
369 REQUIRE_EQ(enemy.ChangeHp(9999, true), 499);
370 REQUIRE_EQ(enemy.GetHp(), 500);
371 REQUIRE_FALSE(enemy.IsDead());
372 }
373 }
374
375 TEST_CASE("ChangeSp") {
376 const MockActor m;
377
378 auto& enemy = MakeEnemy(1, 500, 500, 500, 500, 500, 500);
379
380 REQUIRE_EQ(enemy.GetSp(), 500);
381
382 REQUIRE_EQ(enemy.ChangeSp(-9999), -500);
383 REQUIRE_EQ(enemy.GetSp(), 0);
384
385 REQUIRE_EQ(enemy.ChangeSp(9999), 500);
386 }
387
388 TEST_CASE("ChangeParam") {
389 const MockActor m;
390
391 auto& enemy = MakeEnemy(1, 500, 500, 200, 300, 400, 500);
392
393 REQUIRE_EQ(enemy.GetAtk(), 200);
394 REQUIRE_EQ(enemy.GetDef(), 300);
395 REQUIRE_EQ(enemy.GetSpi(), 400);
396 REQUIRE_EQ(enemy.GetAgi(), 500);
397
398 SUBCASE("atk") {
399 REQUIRE_EQ(enemy.ChangeAtkModifier(-9999), -100);
400 REQUIRE_EQ(enemy.GetAtk(), 100);
401
402 REQUIRE_EQ(enemy.ChangeAtkModifier(9999), 300);
403 REQUIRE_EQ(enemy.GetAtk(), 400);
404 }
405
406 SUBCASE("def") {
407 REQUIRE_EQ(enemy.ChangeDefModifier(-9999), -150);
408 REQUIRE_EQ(enemy.GetDef(), 150);
409
410 REQUIRE_EQ(enemy.ChangeDefModifier(9999), 450);
411 REQUIRE_EQ(enemy.GetDef(), 600);
412 }
413
414 SUBCASE("spi") {
415 REQUIRE_EQ(enemy.ChangeSpiModifier(-9999), -200);
416 REQUIRE_EQ(enemy.GetSpi(), 200);
417
418 REQUIRE_EQ(enemy.ChangeSpiModifier(9999), 600);
419 REQUIRE_EQ(enemy.GetSpi(), 800);
420 }
421
422 SUBCASE("agi") {
423 REQUIRE_EQ(enemy.ChangeAgiModifier(-9999), -250);
424 REQUIRE_EQ(enemy.GetAgi(), 250);
425
426 REQUIRE_EQ(enemy.ChangeAgiModifier(9999), 750);
427 REQUIRE_EQ(enemy.GetAgi(), 1000);
428 }
429 }
430
431 TEST_SUITE_END();
432