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