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 // Headers
19 #include <algorithm>
20 #include <sstream>
21 #include <iterator>
22 #include "game_actor.h"
23 #include "game_battle.h"
24 #include "game_message.h"
25 #include "game_party.h"
26 #include "sprite_actor.h"
27 #include "main_data.h"
28 #include "output.h"
29 #include "player.h"
30 #include <lcf/reader_util.h>
31 #include <lcf/rpg/skill.h>
32 #include "util_macro.h"
33 #include "utils.h"
34 #include "pending_message.h"
35 #include "compiler.h"
36 #include "attribute.h"
37 #include "rand.h"
38 #include "algo.h"
39 
40 constexpr int max_level_2k = 50;
41 constexpr int max_level_2k3 = 99;
42 
MaxHpValue() const43 int Game_Actor::MaxHpValue() const {
44 	auto& val = lcf::Data::system.easyrpg_max_actor_hp;
45 	if (val == -1) {
46 		return Player::IsRPG2k() ? 999 : 9999;
47 	}
48 	return val;
49 }
50 
MaxSpValue() const51 int Game_Actor::MaxSpValue() const {
52 	auto& val = lcf::Data::system.easyrpg_max_actor_sp;
53 	if (val == -1) {
54 		return 999;
55 	}
56 	return val;
57 }
58 
MaxStatBattleValue() const59 int Game_Actor::MaxStatBattleValue() const {
60 	auto& val = lcf::Data::system.easyrpg_max_stat_battle_value;
61 	if (val == -1) {
62 		return 9999;
63 	}
64 	return val;
65 }
66 
MaxStatBaseValue() const67 int Game_Actor::MaxStatBaseValue() const {
68 	auto& val = lcf::Data::system.easyrpg_max_stat_base_value;
69 	if (val == -1) {
70 		return 999;
71 	}
72 	return val;
73 }
74 
MaxExpValue() const75 int Game_Actor::MaxExpValue() const {
76 	auto& val = lcf::Data::system.easyrpg_max_exp;
77 	if (val == -1) {
78 		return Player::IsRPG2k() ? 999999 : 9999999;
79 	}
80 	return val;
81 }
82 
Game_Actor(int actor_id)83 Game_Actor::Game_Actor(int actor_id) {
84 	data.ID = actor_id;
85 	if (actor_id == 0) {
86 		return;
87 	}
88 	dbActor = lcf::ReaderUtil::GetElement(lcf::Data::actors, GetId());
89 
90 	data.two_weapon = dbActor->two_weapon;
91 	data.lock_equipment = dbActor->lock_equipment;
92 	data.auto_battle = dbActor->auto_battle;
93 	data.super_guard = dbActor->super_guard;
94 
95 	data.hp_mod = 0;
96 	data.sp_mod = 0;
97 	data.attack_mod = 0;
98 	data.defense_mod = 0;
99 	data.spirit_mod = 0;
100 	data.agility_mod = 0;
101 
102 	MakeExpList();
103 	SetBattlePosition(GetOriginalPosition());
104 
105 	data.level = 0;
106 	if (dbActor->initial_level > 0) {
107 		// For games like COLORS: Lost Memories which use level 0, don't change level because it'll clamp to 1.
108 		ChangeLevel(dbActor->initial_level, nullptr);
109 	}
110 	SetHp(GetMaxHp());
111 	SetSp(GetMaxSp());
112 
113 	// Remove items that do not exist in the database anymore
114 	std::array<int, 5> ids = {{
115 		dbActor->initial_equipment.weapon_id,
116 		dbActor->initial_equipment.shield_id,
117 		dbActor->initial_equipment.armor_id,
118 		dbActor->initial_equipment.helmet_id,
119 		dbActor->initial_equipment.accessory_id }};
120 	std::replace_if(ids.begin(), ids.end(), [] (const int& item_id) {
121 		return lcf::ReaderUtil::GetElement(lcf::Data::items, item_id) == nullptr;
122 	}, 0);
123 
124 	for (int i = 0; i <= 4; i++) {
125 		SetEquipment(i + 1, ids[i]);
126 	}
127 
128 	data.status.resize(lcf::Data::states.size(), 0);
129 
130 	Fixup();
131 }
132 
SetSaveData(lcf::rpg::SaveActor save)133 void Game_Actor::SetSaveData(lcf::rpg::SaveActor save) {
134 	data = std::move(save);
135 
136 	if (Player::IsRPG2k()) {
137 		data.two_weapon = dbActor->two_weapon;
138 		data.lock_equipment = dbActor->lock_equipment;
139 		data.auto_battle = dbActor->auto_battle;
140 		data.super_guard = dbActor->super_guard;
141 	}
142 
143 	MakeExpList();
144 	Fixup();
145 }
146 
GetSaveData() const147 lcf::rpg::SaveActor Game_Actor::GetSaveData() const {
148 	auto save = data;
149 	if (Player::IsRPG2k()) {
150 		// RPG_RT doesn't save these chunks in rm2k as they are meaningless
151 		save.two_weapon = false;
152 		save.lock_equipment = false;
153 		save.auto_battle = false;
154 		save.super_guard = false;
155 	}
156 	return save;
157 }
158 
Fixup()159 void Game_Actor::Fixup() {
160 	RemoveInvalidData();
161 	ResetEquipmentStates(false);
162 }
163 
UseItem(int item_id,const Game_Battler * source)164 bool Game_Actor::UseItem(int item_id, const Game_Battler* source) {
165 	const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
166 	if (!item) {
167 		Output::Warning("UseItem: Can't use invalid item {}", item_id);
168 		return false;
169 	}
170 
171 	if (!IsDead()) {
172 		if (item->type == lcf::rpg::Item::Type_book) {
173 			return LearnSkill(item->skill_id, nullptr);
174 		}
175 
176 		if (item->type == lcf::rpg::Item::Type_material) {
177 			SetBaseMaxHp(GetBaseMaxHp() + item->max_hp_points);
178 			SetBaseMaxSp(GetBaseMaxSp() + item->max_sp_points);
179 			SetBaseAtk(GetBaseAtk() + item->atk_points2);
180 			SetBaseDef(GetBaseDef() + item->def_points2);
181 			SetBaseAgi(GetBaseAgi() + item->agi_points2);
182 			SetBaseSpi(GetBaseSpi() + item->spi_points2);
183 
184 			return true;
185 		}
186 	}
187 
188 	return Game_Battler::UseItem(item_id, source);
189 }
190 
IsItemUsable(int item_id) const191 bool Game_Actor::IsItemUsable(int item_id) const {
192 	const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
193 	if (!item) {
194 		Output::Warning("IsItemUsable: Invalid item ID {}", item_id);
195 		return false;
196 	}
197 
198 	int query_idx = GetId() - 1;
199 	auto* query_set = &item->actor_set;
200 	if (Player::IsRPG2k3() && lcf::Data::system.equipment_setting == lcf::rpg::System::EquipmentSetting_class) {
201 		auto* cls = GetClass();
202 
203 		// Class index. If there's no class, in the "class_set" it's equal to 0. The first class is 1, not 0
204 		query_idx = cls ? cls->ID : 0;
205 		query_set = &item->class_set;
206 	}
207 
208 	// If the actor or class ID is out of range this is an optimization in the ldb file
209 	// (all actors or classes missing can equip the item)
210 	if (query_set->size() <= (unsigned)(query_idx)) {
211 		return true;
212 	}
213 	return (*query_set)[query_idx];
214 }
215 
IsSkillLearned(int skill_id) const216 bool Game_Actor::IsSkillLearned(int skill_id) const {
217 	return std::find(data.skills.begin(), data.skills.end(), skill_id) != data.skills.end();
218 }
219 
IsSkillUsable(int skill_id) const220 bool Game_Actor::IsSkillUsable(int skill_id) const {
221 	const lcf::rpg::Skill* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, skill_id);
222 	if (!skill) {
223 		Output::Warning("IsSkillUsable: Invalid skill ID {}", skill_id);
224 		return false;
225 	}
226 
227 	if (!skill->affect_attr_defence) {
228 		// Actor must have all attributes of the skill equipped as weapons
229 		const auto* w1 = GetWeapon();
230 		const auto* w2 = Get2ndWeapon();
231 
232 		for (size_t i = 0; i < skill->attribute_effects.size(); ++i) {
233 			bool required = skill->attribute_effects[i] && lcf::Data::attributes[i].type == lcf::rpg::Attribute::Type_physical;
234 			if (required) {
235 				if (w1 && i < w1->attribute_set.size() && w1->attribute_set[i]) {
236 					continue;
237 				}
238 				if (w2 && i < w2->attribute_set.size() && w2->attribute_set[i]) {
239 					continue;
240 				}
241 				return false;
242 			}
243 		}
244 	}
245 
246 	return Game_Battler::IsSkillUsable(skill_id);
247 }
248 
CalculateSkillCost(int skill_id) const249 int Game_Actor::CalculateSkillCost(int skill_id) const {
250 	const lcf::rpg::Skill* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, skill_id);
251 	if (!skill) {
252 		Output::Warning("CalculateSkillCost: Invalid skill ID {}", skill_id);
253 		return 0;
254 	}
255 	return Algo::CalcSkillCost(*skill, GetMaxSp(), HasHalfSpCost());
256 }
257 
LearnSkill(int skill_id,PendingMessage * pm)258 bool Game_Actor::LearnSkill(int skill_id, PendingMessage* pm) {
259 	if (skill_id > 0 && !IsSkillLearned(skill_id)) {
260 		const lcf::rpg::Skill* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, skill_id);
261 		if (!skill) {
262 			Output::Warning("Actor {}: Can't learn invalid skill {}", GetId(), skill_id);
263 			return false;
264 		}
265 
266 		data.skills.push_back((int16_t)skill_id);
267 		std::sort(data.skills.begin(), data.skills.end());
268 
269 		if (pm) {
270 			pm->PushLine(GetLearningMessage(*skill));
271 		}
272 
273 		return true;
274 	}
275 	return false;
276 }
277 
LearnLevelSkills(int min_level,int max_level,PendingMessage * pm)278 int Game_Actor::LearnLevelSkills(int min_level, int max_level, PendingMessage* pm) {
279 	auto& skills = data.class_id > 0 ? GetClass()->skills : dbActor->skills;
280 
281 	int count = 0;
282 
283 	// Learn new skills
284 	for (const lcf::rpg::Learning& learn : skills) {
285 		// Skill learning, up to current level
286 		if (learn.level >= min_level && learn.level <= max_level) {
287 			count += LearnSkill(learn.skill_id, pm);
288 		}
289 	}
290 	return count;
291 }
292 
UnlearnSkill(int skill_id)293 bool Game_Actor::UnlearnSkill(int skill_id) {
294 	std::vector<int16_t>::iterator it = std::find(data.skills.begin(), data.skills.end(), skill_id);
295 	if (it != data.skills.end()) {
296 		data.skills.erase(it);
297 		return true;
298 	}
299 	return false;
300 }
301 
UnlearnAllSkills()302 void Game_Actor::UnlearnAllSkills() {
303 	data.skills.clear();
304 }
305 
SetFace(const std::string & file_name,int index)306 void Game_Actor::SetFace(const std::string& file_name, int index) {
307 	if (file_name == dbActor->face_name && index == dbActor->face_index) {
308 		data.face_name = "";
309 		data.face_id = 0;
310 	} else {
311 		data.face_name.assign(file_name);
312 		data.face_id = index;
313 	}
314 }
315 
GetEquipment(int equip_type) const316 const lcf::rpg::Item* Game_Actor::GetEquipment(int equip_type) const {
317 	if (equip_type <= 0 || equip_type > (int)data.equipped.size())
318 		return nullptr;
319 
320 	int item_id = data.equipped[equip_type - 1];
321 	return lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
322 }
323 
SetEquipment(int equip_type,int new_item_id)324 int Game_Actor::SetEquipment(int equip_type, int new_item_id) {
325 	if (equip_type <= 0 || equip_type > (int) data.equipped.size())
326 		return -1;
327 
328 	int old_item_id = data.equipped[equip_type - 1];
329 	const lcf::rpg::Item* old_item = lcf::ReaderUtil::GetElement(lcf::Data::items, old_item_id);
330 
331 	const lcf::rpg::Item* new_item = lcf::ReaderUtil::GetElement(lcf::Data::items, new_item_id);
332 	if (new_item_id != 0 && !new_item) {
333 		Output::Warning("SetEquipment: Can't equip item with invalid ID {}", new_item_id);
334 		new_item_id = 0;
335 	}
336 
337 	data.equipped[equip_type - 1] = (short)new_item_id;
338 
339 	AdjustEquipmentStates(old_item, false, false);
340 	AdjustEquipmentStates(new_item, true, false);
341 
342 	return old_item_id;
343 }
344 
ChangeEquipment(int equip_type,int item_id)345 void Game_Actor::ChangeEquipment(int equip_type, int item_id) {
346 	int prev_item = SetEquipment(equip_type, item_id);
347 
348 	if (prev_item != 0) {
349 		Main_Data::game_party->AddItem(prev_item, 1);
350 	}
351 	if (item_id != 0) {
352 		Main_Data::game_party->RemoveItem(item_id, 1);
353 	}
354 
355 	// In case you have a two_handed weapon equipped, the other weapon is removed.
356 	const lcf::rpg::Item* item = GetWeapon();
357 	const lcf::rpg::Item* item2 = Get2ndWeapon();
358 	if (item && item2 && (item->two_handed || item2->two_handed)) {
359 		ChangeEquipment(equip_type == lcf::rpg::Item::Type_weapon ? equip_type + 1 : equip_type - 1, 0);
360 	}
361 }
362 
IsEquipped(int equip_id) const363 bool Game_Actor::IsEquipped(int equip_id) const {
364 	for (auto equip : GetWholeEquipment()) {
365 		if (equip == equip_id) {
366 			return true;
367 		}
368 	}
369 	return false;
370 }
371 
RemoveWholeEquipment()372 void Game_Actor::RemoveWholeEquipment() {
373 	for (int i = 1; i <= 5; ++i) {
374 		ChangeEquipment(i, 0);
375 	}
376 }
377 
GetItemCount(int item_id)378 int Game_Actor::GetItemCount(int item_id) {
379 	int number = 0;
380 
381 	if (item_id > 0) {
382 		for (int16_t i : GetWholeEquipment()) {
383 			if (item_id == i) {
384 				++number;
385 			}
386 		}
387 	}
388 
389 	return number;
390 }
391 
FullHeal()392 void Game_Actor::FullHeal() {
393 	RemoveAllStates();
394 	SetHp(GetMaxHp());
395 	SetSp(GetMaxSp());
396 	// Emulates RPG_RT behavior of resetting even battle equipment states on full heal.
397 	ResetEquipmentStates(true);
398 }
399 
GetBaseMaxHp(bool mod) const400 int Game_Actor::GetBaseMaxHp(bool mod) const {
401 	int n = 0;
402 	// Special handling for games that use a level of 0 -> Return 0 Hp
403 	// Same applies for other stats
404 	if (GetLevel() > 0) {
405 		// Looks like RPG_RT only applies Class changes (class_id > 0 - 20kdc)
406 		// when the class was changed by the ChangeClass event, otherwise it uses
407 		// the normal actor attributes.
408 		n = data.class_id > 0
409 			? *lcf::ReaderUtil::GetElement(GetClass()->parameters.maxhp, GetLevel())
410 			: *lcf::ReaderUtil::GetElement(dbActor->parameters.maxhp, GetLevel());
411 	}
412 
413 	if (mod)
414 		n += data.hp_mod;
415 
416 	return Utils::Clamp(n, 1, MaxHpValue());
417 }
418 
GetBaseMaxHp() const419 int Game_Actor::GetBaseMaxHp() const {
420 	return GetBaseMaxHp(true);
421 }
422 
GetBaseMaxSp(bool mod) const423 int Game_Actor::GetBaseMaxSp(bool mod) const {
424 	int n = 0;
425 	if (GetLevel() > 0) {
426 		n = data.class_id > 0
427 			? *lcf::ReaderUtil::GetElement(GetClass()->parameters.maxsp, GetLevel())
428 			: *lcf::ReaderUtil::GetElement(dbActor->parameters.maxsp, GetLevel());
429 	}
430 
431 	if (mod)
432 		n += data.sp_mod;
433 
434 	return Utils::Clamp(n, 0, MaxSpValue());
435 }
436 
GetBaseMaxSp() const437 int Game_Actor::GetBaseMaxSp() const {
438 	return GetBaseMaxSp(true);
439 }
440 
IsArmorType(const lcf::rpg::Item * item)441 static bool IsArmorType(const lcf::rpg::Item* item) {
442 	return item->type == lcf::rpg::Item::Type_shield
443 		|| item->type == lcf::rpg::Item::Type_armor
444 		|| item->type == lcf::rpg::Item::Type_helmet
445 		|| item->type == lcf::rpg::Item::Type_accessory;
446 }
447 
448 template <bool allow_weapon, bool allow_armor, typename F>
ForEachEquipment(Span<const short> equipped,F && f,Game_Battler::Weapon weapon=Game_Battler::WeaponAll)449 void ForEachEquipment(Span<const short> equipped, F&& f, Game_Battler::Weapon weapon = Game_Battler::WeaponAll) {
450 	for (int slot = 0; slot < static_cast<int>(equipped.size()); ++slot) {
451 		const auto item_id = equipped[slot];
452 		if (item_id <= 0) {
453 			continue;
454 		}
455 
456 		auto* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
457 		// Invalid equipment was removed
458 		assert(item != nullptr);
459 
460 		if (item->type == lcf::rpg::Item::Type_weapon) {
461 			if (!allow_weapon || (weapon != Game_Battler::WeaponAll && weapon != slot + 1)) {
462 				continue;
463 			}
464 		} else if (IsArmorType(item)) {
465 			if (!allow_armor) {
466 				continue;
467 			}
468 		} else {
469 			assert(false && "Invalid item type equipped!");
470 			continue;
471 		}
472 
473 		f(*item);
474 	}
475 }
476 
GetBaseAtk(Weapon weapon,bool mod,bool equip) const477 int Game_Actor::GetBaseAtk(Weapon weapon, bool mod, bool equip) const {
478 	int n = 0;
479 	if (GetLevel() > 0) {
480 		n = data.class_id > 0
481 			? *lcf::ReaderUtil::GetElement(GetClass()->parameters.attack, GetLevel())
482 			: *lcf::ReaderUtil::GetElement(dbActor->parameters.attack, GetLevel());
483 	}
484 
485 	if (mod) {
486 		n += data.attack_mod;
487 	}
488 
489 	if (equip) {
490 		ForEachEquipment<true,true>(GetWholeEquipment(), [&](auto& item) { n += item.atk_points1; }, weapon);
491 	}
492 
493 	return Utils::Clamp(n, 1, MaxStatBaseValue());
494 }
495 
GetBaseAtk(Weapon weapon) const496 int Game_Actor::GetBaseAtk(Weapon weapon) const {
497 	return GetBaseAtk(weapon, true, true);
498 }
499 
GetBaseDef(Weapon weapon,bool mod,bool equip) const500 int Game_Actor::GetBaseDef(Weapon weapon, bool mod, bool equip) const {
501 	int n = 0;
502 	if (GetLevel() > 0) {
503 		n = data.class_id > 0
504 			? *lcf::ReaderUtil::GetElement(GetClass()->parameters.defense, GetLevel())
505 			: *lcf::ReaderUtil::GetElement(dbActor->parameters.defense, GetLevel());
506 	}
507 
508 	if (mod) {
509 		n += data.defense_mod;
510 	}
511 
512 	if (equip) {
513 		ForEachEquipment<true,true>(GetWholeEquipment(), [&](auto& item) { n += item.def_points1; }, weapon);
514 	}
515 
516 	return Utils::Clamp(n, 1, MaxStatBaseValue());
517 }
518 
GetBaseDef(Weapon weapon) const519 int Game_Actor::GetBaseDef(Weapon weapon) const {
520 	return GetBaseDef(weapon, true, true);
521 }
522 
GetBaseSpi(Weapon weapon,bool mod,bool equip) const523 int Game_Actor::GetBaseSpi(Weapon weapon, bool mod, bool equip) const {
524 	int n = 0;
525 	if (GetLevel() > 0) {
526 		n = data.class_id > 0
527 			? *lcf::ReaderUtil::GetElement(GetClass()->parameters.spirit, GetLevel())
528 			: *lcf::ReaderUtil::GetElement(dbActor->parameters.spirit, GetLevel());
529 	}
530 
531 	if (mod) {
532 		n += data.spirit_mod;
533 	}
534 
535 	if (equip) {
536 		ForEachEquipment<true,true>(GetWholeEquipment(), [&](auto& item) { n += item.spi_points1; }, weapon);
537 	}
538 
539 	return Utils::Clamp(n, 1, MaxStatBaseValue());
540 }
541 
GetBaseSpi(Weapon weapon) const542 int Game_Actor::GetBaseSpi(Weapon weapon) const {
543 	return GetBaseSpi(weapon, true, true);
544 }
545 
GetBaseAgi(Weapon weapon,bool mod,bool equip) const546 int Game_Actor::GetBaseAgi(Weapon weapon, bool mod, bool equip) const {
547 	int n = 0;
548 	if (GetLevel() > 0) {
549 		n = data.class_id > 0
550 			? *lcf::ReaderUtil::GetElement(GetClass()->parameters.agility, GetLevel())
551 			: *lcf::ReaderUtil::GetElement(dbActor->parameters.agility, GetLevel());
552 	}
553 
554 	if (mod) {
555 		n += data.agility_mod;
556 	}
557 
558 	if (equip) {
559 		ForEachEquipment<true,true>(GetWholeEquipment(), [&](auto& item) { n += item.agi_points1; }, weapon);
560 	}
561 
562 	return Utils::Clamp(n, 1, MaxStatBaseValue());
563 }
564 
GetBaseAgi(Weapon weapon) const565 int Game_Actor::GetBaseAgi(Weapon weapon) const {
566 	return GetBaseAgi(weapon, true, true);
567 }
568 
CalculateExp(int level) const569 int Game_Actor::CalculateExp(int level) const {
570 	const lcf::rpg::Class* klass = lcf::ReaderUtil::GetElement(lcf::Data::classes, data.class_id);
571 
572 	int exp_curve = Player::IsRPG2k() ? 1 : 2;
573 	if (lcf::Data::system.easyrpg_alternative_exp > 0) {
574 		exp_curve = lcf::Data::system.easyrpg_alternative_exp;
575 	}
576 
577 	double base, inflation, correction;
578 	if (klass) {
579 		base = klass->exp_base;
580 		inflation = klass->exp_inflation;
581 		correction = klass->exp_correction;
582 	}
583 	else {
584 		const lcf::rpg::Actor& actor = *lcf::ReaderUtil::GetElement(lcf::Data::actors, GetId());
585 		base = actor.exp_base;
586 		inflation = actor.exp_inflation;
587 		correction = actor.exp_correction;
588 	}
589 
590 	int result = 0;
591 	if (exp_curve == 1) {
592 		inflation = 1.5 + (inflation * 0.01);
593 
594 		for (int i = level; i >= 1; i--)
595 		{
596 			result = result + (int)(correction + base);
597 			base = base * inflation;
598 			inflation = ((level+1) * 0.002 + 0.8) * (inflation - 1) + 1;
599 		}
600 	} else /*Rpg2k3*/ {
601 		for (int i = 1; i <= level; i++)
602 		{
603 			result += (int)base;
604 			result += i * (int)inflation;
605 			result += (int)correction;
606 		}
607 	}
608 	return min(result, MaxExpValue());
609 }
610 
MakeExpList()611 void Game_Actor::MakeExpList() {
612 	exp_list.resize((size_t)GetMaxLevel());
613 	for (int i = 1; i < (int)exp_list.size(); ++i) {
614 		exp_list[i] = CalculateExp(i);
615 	}
616 }
617 
GetExpString(bool status_scene) const618 std::string Game_Actor::GetExpString(bool status_scene) const {
619 	// RPG_RT displays dashes for max level. As a customization
620 	// we always display the amount of EXP.
621 	// if (GetNextExp() == -1) { return (MaxExpValue() >= 1000000 || status_scene) ? "-------" : "------"; }
622 	return std::to_string(GetExp());
623 }
624 
GetNextExpString(bool status_scene) const625 std::string Game_Actor::GetNextExpString(bool status_scene) const {
626 	if (GetNextExp() == -1) {
627 		return (MaxExpValue() >= 1000000 || status_scene) ? "-------" : "------";
628 	}
629 	return std::to_string(GetNextExp());
630 }
631 
GetBaseExp() const632 int Game_Actor::GetBaseExp() const {
633 	return GetBaseExp(GetLevel());
634 }
635 
GetBaseExp(int level) const636 int Game_Actor::GetBaseExp(int level) const {
637 	return GetNextExp(level - 1);
638 }
639 
GetNextExp() const640 int Game_Actor::GetNextExp() const {
641 	return GetNextExp(GetLevel());
642 }
643 
GetNextExp(int level) const644 int Game_Actor::GetNextExp(int level) const {
645 	if (level >= GetMaxLevel() || level <= -1) {
646 		return -1;
647 	} else if (level == 0) {
648 		return 0;
649 	} else {
650 		return exp_list[level];
651 	}
652 }
653 
GetStateProbability(int state_id) const654 int Game_Actor::GetStateProbability(int state_id) const {
655 	int rate = 2, mul = 100; // C - default
656 
657 	const uint8_t* r = lcf::ReaderUtil::GetElement(dbActor->state_ranks, state_id);
658 	if (r) {
659 		rate = *r;
660 	}
661 
662 	// This takes the armor of the character with the most resistance for that particular state
663 	for (const auto equipment : GetWholeEquipment()) {
664 		lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, equipment);
665 		if (item != nullptr
666 				&& !(Player::IsRPG2k3() && item->reverse_state_effect)
667 				&& (item->type == lcf::rpg::Item::Type_shield || item->type == lcf::rpg::Item::Type_armor
668 			|| item->type == lcf::rpg::Item::Type_helmet || item->type == lcf::rpg::Item::Type_accessory)
669 			&& state_id  <= static_cast<int>(item->state_set.size()) && item->state_set[state_id - 1]) {
670 			mul = std::min<int>(mul, 100 - item->state_chance);
671 		}
672 	}
673 
674 	// GetStateRate verifies the state_id
675 	return GetStateRate(state_id, rate) * mul / 100;
676 }
677 
GetBaseAttributeRate(int attribute_id) const678 int Game_Actor::GetBaseAttributeRate(int attribute_id) const {
679 	int rate = 2; // C - default
680 
681 	const auto* r = lcf::ReaderUtil::GetElement(dbActor->attribute_ranks, attribute_id);
682 	if (r) {
683 		rate = *r;
684 	}
685 
686 	bool boost = false;
687 	ForEachEquipment<false,true>(GetWholeEquipment(), [&](auto& item) {
688 			boost |= attribute_id >= 1 && attribute_id <= static_cast<int>(item.attribute_set.size()) && item.attribute_set[attribute_id - 1];
689 			});
690 	rate += boost;
691 
692 	return Utils::Clamp(rate, 0, 4);
693 }
694 
GetWeaponId() const695 int Game_Actor::GetWeaponId() const {
696 	int item_id = GetWholeEquipment()[0];
697 	return item_id <= (int)lcf::Data::items.size() ? item_id : 0;
698 }
699 
GetShieldId() const700 int Game_Actor::GetShieldId() const {
701 	int item_id = GetWholeEquipment()[1];
702 	return item_id <= (int)lcf::Data::items.size() ? item_id : 0;
703 }
704 
GetArmorId() const705 int Game_Actor::GetArmorId() const {
706 	int item_id = GetWholeEquipment()[2];
707 	return item_id <= (int)lcf::Data::items.size() ? item_id : 0;
708 }
709 
GetHelmetId() const710 int Game_Actor::GetHelmetId() const {
711 	int item_id = GetWholeEquipment()[3];
712 	return item_id <= (int)lcf::Data::items.size() ? item_id : 0;
713 }
714 
GetAccessoryId() const715 int Game_Actor::GetAccessoryId() const {
716 	int item_id = GetWholeEquipment()[4];
717 	return item_id <= (int)lcf::Data::items.size() ? item_id : 0;
718 }
719 
GetMaxLevel() const720 int Game_Actor::GetMaxLevel() const {
721 	int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3;
722 	if (lcf::Data::system.easyrpg_max_level > -1) {
723 		max_level = lcf::Data::system.easyrpg_max_level;
724 	}
725 	return Utils::Clamp<int32_t>(max_level, 1, dbActor->final_level);
726 }
727 
SetExp(int _exp)728 void Game_Actor::SetExp(int _exp) {
729 	data.exp = Utils::Clamp<int32_t>(_exp, 0, MaxExpValue());
730 }
731 
ChangeExp(int exp,PendingMessage * pm)732 void Game_Actor::ChangeExp(int exp, PendingMessage* pm) {
733 	int new_level = GetLevel();
734 	int new_exp = Utils::Clamp<int>(exp, 0, MaxExpValue());
735 
736 	if (new_exp > GetExp()) {
737 		for (int i = GetLevel() + 1; i <= GetMaxLevel(); ++i) {
738 			if (GetNextExp(new_level) != -1 && GetNextExp(new_level) > new_exp) {
739 				break;
740 			}
741 			new_level++;
742 		}
743 	} else if (new_exp < GetExp()) {
744 		for (int i = GetLevel(); i > 1; --i) {
745 			if (new_exp >= GetNextExp(i - 1)) {
746 				break;
747 			}
748 			new_level--;
749 		}
750 	}
751 
752 	SetExp(new_exp);
753 
754 	if (new_level != GetLevel()) {
755 		ChangeLevel(new_level, pm);
756 	}
757 }
758 
SetLevel(int _level)759 void Game_Actor::SetLevel(int _level) {
760 	data.level = Utils::Clamp(_level, 1, GetMaxLevel());
761 	// Ensure current HP/SP remain clamped if new Max HP/SP is less.
762 	SetHp(GetHp());
763 	SetSp(GetSp());
764 
765 }
766 
GetLevelUpMessage(int new_level) const767 std::string Game_Actor::GetLevelUpMessage(int new_level) const {
768 	std::stringstream ss;
769 	if (Player::IsRPG2k3E()) {
770 		ss << GetName();
771 		ss << " " << lcf::Data::terms.level_up << " ";
772 		ss << " " << lcf::Data::terms.level << " " << new_level;
773 		return ss.str();
774 	} else if (Player::IsRPG2kE()) {
775 		ss << new_level;
776 		return Utils::ReplacePlaceholders(
777 			lcf::Data::terms.level_up,
778 			Utils::MakeArray('S', 'V', 'U'),
779 			Utils::MakeSvArray(GetName(), ss.str(), lcf::Data::terms.level)
780 		);
781 	} else {
782 		std::string particle, space = "";
783 		if (Player::IsCP932()) {
784 			particle = "は";
785 			space += " ";
786 		}
787 		else {
788 			particle = " ";
789 		}
790 		ss << GetName();
791 		ss << particle << lcf::Data::terms.level << " ";
792 		ss << new_level << space << lcf::Data::terms.level_up;
793 		return ss.str();
794 	}
795 }
796 
GetLearningMessage(const lcf::rpg::Skill & skill) const797 std::string Game_Actor::GetLearningMessage(const lcf::rpg::Skill& skill) const {
798 	if (Player::IsRPG2kE()) {
799 		return Utils::ReplacePlaceholders(
800 			lcf::Data::terms.skill_learned,
801 			Utils::MakeArray('S', 'O'),
802 			Utils::MakeSvArray(GetName(), skill.name)
803 		);
804 	}
805 
806 	return ToString(skill.name) + (Player::IsRPG2k3E() ? " " : "") + ToString(lcf::Data::terms.skill_learned);
807 }
808 
ChangeLevel(int new_level,PendingMessage * pm)809 void Game_Actor::ChangeLevel(int new_level, PendingMessage* pm) {
810 	int old_level = GetLevel();
811 	SetLevel(new_level);
812 	new_level = GetLevel(); // Level adjusted to max
813 
814 	if (new_level > old_level) {
815 		if (pm) {
816 			pm->PushLine(GetLevelUpMessage(new_level));
817 		}
818 
819 		// Learn new skills
820 		LearnLevelSkills(old_level + 1, new_level, pm);
821 
822 		if (pm) {
823 			pm->PushPageEnd();
824 		}
825 
826 		// Experience adjustment:
827 		// At least level minimum
828 		SetExp(max(GetBaseExp(), GetExp()));
829 	} else if (new_level < old_level) {
830 		// Experience adjustment:
831 		// Level minimum if higher then Level maximum
832 		if (GetExp() >= GetNextExp()) {
833 			SetExp(GetBaseExp());
834 		}
835 	}
836 }
837 
IsEquippable(int item_id) const838 bool Game_Actor::IsEquippable(int item_id) const {
839 	const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
840 	if (!item) {
841 		Output::Warning("IsEquippable: Invalid item ID {}", item_id);
842 		return false;
843 	}
844 
845 	if (HasTwoWeapons() &&
846 		item->type == lcf::rpg::Item::Type_shield) {
847 			return false;
848 	}
849 
850 	return IsItemUsable(item_id);
851 }
852 
IsEquipmentFixed(bool check_states) const853 bool Game_Actor::IsEquipmentFixed(bool check_states) const {
854 	if (data.lock_equipment) {
855 		return true;
856 	}
857 
858 	if (check_states) {
859 		for (auto state_id: GetInflictedStates()) {
860 			auto* state = lcf::ReaderUtil::GetElement(lcf::Data::states, state_id);
861 			if (state && state->cursed) {
862 				return true;
863 			}
864 		}
865 	}
866 	return false;
867 }
868 
GetRandomSkill() const869 const lcf::rpg::Skill* Game_Actor::GetRandomSkill() const {
870 	const std::vector<int16_t>& skills = GetSkills();
871 	if (skills.empty()) {
872 		return nullptr;
873 	}
874 
875 	// Skills are guaranteed to be valid
876 	return lcf::ReaderUtil::GetElement(lcf::Data::skills, skills[Rand::GetRandomNumber(0, skills.size() - 1)]);
877 }
878 
GetOriginalPosition() const879 Point Game_Actor::GetOriginalPosition() const {
880 	return { dbActor->battle_x, dbActor->battle_y };
881 }
882 
GetSkillName() const883 StringView Game_Actor::GetSkillName() const {
884 	return dbActor->rename_skill ? StringView(dbActor->skill_name) : StringView(lcf::Data::terms.command_skill);
885 }
886 
SetSprite(const std::string & file,int index,bool transparent)887 void Game_Actor::SetSprite(const std::string &file, int index, bool transparent) {
888 	if (file == dbActor->character_name
889 			&& index == dbActor->character_index
890 			&& transparent == dbActor->transparent) {
891 		data.sprite_name = "";
892 		data.sprite_id = 0;
893 		data.transparency = 0;
894 	} else {
895 		data.sprite_name = file;
896 		data.sprite_id = index;
897 		data.transparency = transparent ? 3 : 0;
898 	}
899 }
900 
ChangeBattleCommands(bool add,int id)901 void Game_Actor::ChangeBattleCommands(bool add, int id) {
902 	auto& cmds = data.battle_commands;
903 
904 	// If changing battle commands, that is when RPG_RT will replace the -1 list with a 'true' list.
905 	// Fetch original command array.
906 	if (!data.changed_battle_commands) {
907 		cmds = lcf::Data::actors[GetId() - 1].battle_commands;
908 		data.changed_battle_commands = true;
909 	}
910 
911 	// The battle commands array always has a size of 7 padded with -1. The last element before the padding is 0 which
912 	// stands for the Row command
913 	if (add) {
914 		const lcf::rpg::BattleCommand* cmd = lcf::ReaderUtil::GetElement(lcf::Data::battlecommands.commands, id);
915 		if (!cmd) {
916 			Output::Warning("ChangeBattleCommands: Can't add invalid battle command {}", id);
917 			return;
918 		}
919 
920 		if (std::find(cmds.begin(), cmds.end(), id)	== cmds.end()) {
921 			std::vector<int32_t> new_cmds;
922 			std::copy_if(cmds.begin(), cmds.end(),
923 						 std::back_inserter(new_cmds), [](int32_t i) { return i != 0 && i != -1; });
924 			// Needs space for at least 2 more commands (new command and row)
925 			if (new_cmds.size() >= 6) {
926 				return;
927 			}
928 			new_cmds.push_back(id);
929 			std::sort(new_cmds.begin(), new_cmds.end());
930 			new_cmds.push_back(0);
931 			cmds = new_cmds;
932 		}
933 	} else if (id == 0) {
934 		cmds.clear();
935 		cmds.push_back(0);
936 	} else {
937 		std::vector<int32_t>::iterator it;
938 		it = std::find(cmds.begin(), cmds.end(), id);
939 		if (it != cmds.end())
940 			cmds.erase(it);
941 	}
942 
943 	cmds.resize(7, -1);
944 }
945 
GetBattleCommand(int idx) const946 const lcf::rpg::BattleCommand* Game_Actor::GetBattleCommand(int idx) const {
947 	Span<const int32_t> commands;
948 	if (data.changed_battle_commands) {
949 		commands = data.battle_commands;
950 	} else if (dbActor) {
951 		commands = dbActor->battle_commands;
952 	}
953 	int cmd_id = 0;
954 	if (idx >= 0 && idx < static_cast<int>(commands.size())) {
955 		cmd_id = commands[idx];
956 	}
957 	return lcf::ReaderUtil::GetElement(lcf::Data::battlecommands.commands, cmd_id);
958 }
959 
GetBattleCommands() const960 const std::vector<const lcf::rpg::BattleCommand*> Game_Actor::GetBattleCommands() const {
961 	std::vector<const lcf::rpg::BattleCommand*> commands;
962 	std::vector<int32_t> obc = data.battle_commands;
963 	if (!data.changed_battle_commands) {
964 		// In this case, get it straight from the LDB.
965 		obc = lcf::Data::actors[GetId() - 1].battle_commands;
966 	}
967 
968 	for (int command_index : obc) {
969 		if (command_index == 0) {
970 			// Row command -> not impl
971 			continue;
972 		}
973 
974 		if (command_index == -1) {
975 			// Empty slot
976 			continue;
977 		}
978 
979 		const lcf::rpg::BattleCommand* cmd = lcf::ReaderUtil::GetElement(lcf::Data::battlecommands.commands, command_index);
980 		if (!cmd) {
981 			Output::Warning("GetBattleCommands: Invalid battle command ID {}", command_index);
982 			continue;
983 		}
984 
985 		commands.push_back(cmd);
986 	}
987 
988 	return commands;
989 }
990 
GetClass() const991 const lcf::rpg::Class* Game_Actor::GetClass() const {
992 	int id = data.class_id;
993 
994 	if (id < 0) {
995 		// This means class ID hasn't been changed yet.
996 		id = dbActor->class_id;
997 	}
998 
999 	return lcf::ReaderUtil::GetElement(lcf::Data::classes, id);
1000 }
1001 
ChangeClass(int new_class_id,int new_level,ClassChangeSkillMode new_skill,ClassChangeParamMode new_param,PendingMessage * pm)1002 void Game_Actor::ChangeClass(int new_class_id,
1003 		int new_level,
1004 		ClassChangeSkillMode new_skill,
1005 		ClassChangeParamMode new_param,
1006 		PendingMessage* pm
1007 		)
1008 {
1009 	const auto* cls = lcf::ReaderUtil::GetElement(lcf::Data::classes, new_class_id);
1010 	if (new_class_id != 0 && cls == nullptr) {
1011 		Output::Warning("Actor {}: Can't change to invalid class {}", GetId(), new_class_id);
1012 		return;
1013 	}
1014 
1015 	// RPG_RT always removes all equipment on level change.
1016 	RemoveWholeEquipment();
1017 
1018 	const auto prev_level = GetLevel();
1019 	const auto hp = GetHp();
1020 	const auto sp = GetSp();
1021 
1022 	auto max_hp = GetBaseMaxHp();
1023 	auto max_sp = GetBaseMaxSp();
1024 	auto atk = GetBaseAtk();
1025 	auto def = GetBaseDef();
1026 	auto spi = GetBaseSpi();
1027 	auto agi = GetBaseAgi();
1028 
1029 	SetLevel(1);
1030 	data.hp_mod = 0;
1031 	data.sp_mod = 0;
1032 	data.attack_mod = 0;
1033 	data.defense_mod = 0;
1034 	data.spirit_mod = 0;
1035 	data.agility_mod = 0;
1036 
1037 	data.class_id = new_class_id;
1038 	data.changed_battle_commands = true; // Any change counts as a battle commands change.
1039 
1040 	// The class settings are not applied when the actor has a class on startup
1041 	// but only when the "Change Class" event command is used.
1042 
1043 	if (cls) {
1044 		data.super_guard = cls->super_guard;
1045 		data.lock_equipment = cls->lock_equipment;
1046 		data.two_weapon = cls->two_weapon;
1047 		data.auto_battle = cls->auto_battle;
1048 
1049 		data.battler_animation = cls->battler_animation;
1050 
1051 		data.battle_commands = cls->battle_commands;
1052 	} else {
1053 		data.super_guard = dbActor->super_guard;
1054 		data.lock_equipment = dbActor->lock_equipment;
1055 		data.two_weapon = dbActor->two_weapon;
1056 		data.auto_battle = dbActor->auto_battle;
1057 
1058 		data.battler_animation = 0;
1059 
1060 		data.battle_commands = dbActor->battle_commands;
1061 	}
1062 
1063 	MakeExpList();
1064 
1065 	switch (new_param) {
1066 		case eParamNoChange:
1067 			break;
1068 		case eParamHalf:
1069 			max_hp /= 2;
1070 			max_sp /= 2;
1071 			atk /= 2;
1072 			def /= 2;
1073 			spi /= 2;
1074 			agi /= 2;
1075 			break;
1076 		case eParamResetLevel1:
1077 			max_hp = GetBaseMaxHp();
1078 			max_sp = GetBaseMaxSp();
1079 			atk = GetBaseAtk();
1080 			def = GetBaseDef();
1081 			spi = GetBaseSpi();
1082 			agi = GetBaseAgi();
1083 			break;
1084 		case eParamReset:
1085 			break;
1086 	}
1087 
1088 	SetLevel(new_level);
1089 	if (pm && new_level > 1 && (new_level > prev_level || new_skill != eSkillNoChange)) {
1090 		pm->PushLine(GetLevelUpMessage(new_level));
1091 	}
1092 
1093 	// RPG_RT always resets EXP when class is changed, even if level unchanged.
1094 	SetExp(GetBaseExp());
1095 
1096 	if (new_param != eParamReset) {
1097 		SetBaseMaxHp(max_hp);
1098 		SetBaseMaxSp(max_sp);
1099 		SetBaseAtk(atk);
1100 		SetBaseDef(def);
1101 		SetBaseSpi(spi);
1102 		SetBaseAgi(agi);
1103 	}
1104 
1105 	SetHp(hp);
1106 	SetSp(sp);
1107 
1108 	switch (new_skill) {
1109 		case eSkillNoChange:
1110 			break;
1111 		case eSkillReset:
1112 			// RPG_RT has a bug where if (new_level == 1 && new_class_id == prev_class_id) no skills are removed.
1113 			UnlearnAllSkills();
1114 			// fall through
1115 		case eSkillAdd:
1116 			// RPG_RT has a bug where if (new_class_id == prev_class_id) level 1 skills are not learned.
1117 			LearnLevelSkills(1, new_level, pm);
1118 			break;
1119 	}
1120 }
1121 
GetClassName() const1122 StringView Game_Actor::GetClassName() const {
1123     if (!GetClass()) {
1124         return {};
1125     }
1126     return GetClass()->name;
1127 }
1128 
ClampMaxHpMod(int hp,const Game_Actor * actor)1129 static int ClampMaxHpMod(int hp, const Game_Actor* actor) {
1130 	auto limit = actor->MaxHpValue();
1131 	return Utils::Clamp(hp, -limit, limit);
1132 }
1133 
ClampMaxSpMod(int sp,const Game_Actor * actor)1134 static int ClampMaxSpMod(int sp, const Game_Actor* actor) {
1135 	auto limit = actor->MaxSpValue();
1136 	return Utils::Clamp(sp, -limit, limit);
1137 }
1138 
ClampStatMod(int value,const Game_Actor * actor)1139 static int ClampStatMod(int value, const Game_Actor* actor) {
1140 	auto limit = actor->MaxStatBaseValue();
1141 	return Utils::Clamp(value, -limit, limit);
1142 }
1143 
SetBaseMaxHp(int maxhp)1144 void Game_Actor::SetBaseMaxHp(int maxhp) {
1145 	int new_hp_mod = data.hp_mod + (maxhp - GetBaseMaxHp());
1146 	data.hp_mod = ClampMaxHpMod(new_hp_mod, this);
1147 
1148 	SetHp(data.current_hp);
1149 }
1150 
SetBaseMaxSp(int maxsp)1151 void Game_Actor::SetBaseMaxSp(int maxsp) {
1152 	int new_sp_mod = data.sp_mod + (maxsp - GetBaseMaxSp());
1153 	data.sp_mod = ClampMaxSpMod(new_sp_mod, this);
1154 
1155 	SetSp(data.current_sp);
1156 }
1157 
SetHp(int hp)1158 int Game_Actor::SetHp(int hp) {
1159 	data.current_hp = Utils::Clamp(hp, 0, GetMaxHp());
1160 	return data.current_hp;
1161 }
1162 
SetSp(int sp)1163 int Game_Actor::SetSp(int sp) {
1164 	data.current_sp = Utils::Clamp(sp, 0, GetMaxSp());
1165 	return data.current_sp;
1166 }
1167 
SetBaseAtk(int atk)1168 void Game_Actor::SetBaseAtk(int atk) {
1169 	int new_attack_mod = data.attack_mod + (atk - GetBaseAtk());
1170 	data.attack_mod = ClampStatMod(new_attack_mod, this);
1171 }
1172 
SetBaseDef(int def)1173 void Game_Actor::SetBaseDef(int def) {
1174 	int new_defense_mod = data.defense_mod + (def - GetBaseDef());
1175 	data.defense_mod = ClampStatMod(new_defense_mod, this);
1176 }
1177 
SetBaseSpi(int spi)1178 void Game_Actor::SetBaseSpi(int spi) {
1179 	int new_spirit_mod = data.spirit_mod + (spi - GetBaseSpi());
1180 	data.spirit_mod = ClampStatMod(new_spirit_mod, this);
1181 }
1182 
SetBaseAgi(int agi)1183 void Game_Actor::SetBaseAgi(int agi) {
1184 	int new_agility_mod = data.agility_mod + (agi - GetBaseAgi());
1185 	data.agility_mod = ClampStatMod(new_agility_mod, this);
1186 }
1187 
GetBattleRow() const1188 Game_Actor::RowType Game_Actor::GetBattleRow() const {
1189 	return RowType(data.row);
1190 }
1191 
SetBattleRow(RowType battle_row)1192 void Game_Actor::SetBattleRow(RowType battle_row) {
1193 	data.row = int(battle_row);
1194 }
1195 
GetBattleAnimationId() const1196 int Game_Actor::GetBattleAnimationId() const {
1197 	if (Player::IsRPG2k()) {
1198 		return 0;
1199 	}
1200 
1201 	int anim = 0;
1202 
1203 	if (data.battler_animation <= 0) {
1204 		// Earlier versions of EasyRPG didn't save this value correctly
1205 
1206 		// The battle animation of the class only matters when the class was
1207 		// changed by event "Change Class"
1208 		if ((data.class_id > 0) && GetClass()) {
1209 			anim = GetClass()->battler_animation;
1210 		} else {
1211 			const lcf::rpg::BattlerAnimation* anima = lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, dbActor->battler_animation);
1212 			if (!anima) {
1213 				Output::Warning("Actor {}: Invalid battle animation ID {}", GetId(), dbActor->battler_animation);
1214 				return 0;
1215 			}
1216 
1217 			anim = anima->ID;
1218 		}
1219 	} else {
1220 		anim = data.battler_animation;
1221 	}
1222 
1223 	if (anim == 0) {
1224 		// Chunk was missing, set to proper default
1225 		return 1;
1226 	}
1227 
1228 	return anim;
1229 }
1230 
GetHitChance(Weapon weapon) const1231 int Game_Actor::GetHitChance(Weapon weapon) const {
1232 	int hit = INT_MIN;
1233 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { hit = std::max(hit, static_cast<int>(item.hit)); }, weapon);
1234 
1235 	return hit != INT_MIN ? hit : 90;
1236 }
1237 
GetCriticalHitChance(Weapon weapon) const1238 float Game_Actor::GetCriticalHitChance(Weapon weapon) const {
1239 	float crit_chance = dbActor->critical_hit ? 1.0f / dbActor->critical_hit_chance : 0.0f;
1240 
1241 	float bonus = 0;
1242 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { bonus = std::max(bonus, static_cast<float>(item.critical_hit)); }, weapon);
1243 	return crit_chance + (bonus / 100.0f);
1244 }
1245 
IsControllable() const1246 int Game_Actor::IsControllable() const {
1247 	return GetSignificantRestriction() == lcf::rpg::State::Restriction_normal && !GetAutoBattle();
1248 }
1249 
1250 
RemoveInvalidData()1251 void Game_Actor::RemoveInvalidData() {
1252 	/*
1253 	 The following actor data is cleaned up:
1254 	 - Invalid equipment is removed
1255 	 - An invalid class is removed
1256 	 - Invalid states are removed
1257 	 - Level is between 0 and 99, and does not exceed MaxLevel
1258 
1259 	 For "external data" (not from LCF Actor or LSD SaveActor) the data is
1260 	 verified in the corresponding functions.
1261 	*/
1262 
1263 	// Filter out invalid equipment
1264 	int eq_types[] = { lcf::rpg::Item::Type_weapon,
1265 		HasTwoWeapons() ? lcf::rpg::Item::Type_weapon : lcf::rpg::Item::Type_shield,
1266 		lcf::rpg::Item::Type_armor,
1267 		lcf::rpg::Item::Type_helmet,
1268 		lcf::rpg::Item::Type_accessory
1269 	};
1270 
1271 	auto& equipment = GetWholeEquipment();
1272 	for (size_t i = 0; i < equipment.size(); ++i) {
1273 		int eq_id = equipment[i];
1274 		lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, eq_id);
1275 
1276 		if (!item && eq_id != 0) {
1277 			Output::Debug("Actor {}: Removing invalid item {} from equipment slot {}",
1278 			GetId(), eq_id, eq_types[i]);
1279 			SetEquipment(i + 1, 0);
1280 		} else if (item && item->type != eq_types[i]) {
1281 			Output::Debug("Actor {}: Removing item {} (of type {}) from equipment slot {} (needs type {})",
1282 			GetId(), item->ID, item->type, i + 1, eq_types[i]);
1283 			SetEquipment(i + 1, 0);
1284 		} else if (item && !IsItemUsable(item->ID)) {
1285 			Output::Debug("Actor {}: Removing item {} from equipment slot {} (Not equippable by this actor)",
1286 			GetId(), item->ID, i + 1);
1287 			SetEquipment(i + 1, 0);
1288 		}
1289 	}
1290 
1291 	// Remove invalid class
1292 	if (data.class_id > 0) {
1293 		const lcf::rpg::Class* cls = lcf::ReaderUtil::GetElement(lcf::Data::classes, data.class_id);
1294 		if (!cls) {
1295 			Output::Warning("Actor {}: Removing invalid class {}", GetId(), data.class_id);
1296 			ChangeClass(0, GetLevel(), eSkillNoChange, eParamNoChange, nullptr);
1297 		}
1298 	}
1299 
1300 	// Remove invalid skills
1301 	for (int16_t skill_id : GetSkills()) {
1302 		const lcf::rpg::Skill* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, skill_id);
1303 		if (!skill) {
1304 			Output::Warning("Actor {}: Removing invalid skill {}", GetId(), skill_id);
1305 			UnlearnSkill(skill_id);
1306 		}
1307 	}
1308 
1309 	// Remove invalid states
1310 	if (GetStates().size() > lcf::Data::states.size()) {
1311 		Output::Warning("Actor {}: State array contains invalid states ({} > {})", GetId(), GetStates().size(), lcf::Data::states.size());
1312 		GetStates().resize(lcf::Data::states.size());
1313 	}
1314 
1315 	// Remove invalid levels
1316 	// Special handling for the game COLORS: Lost Memories which uses level 0
1317 	// through database editing. Hopefully no game uses negative levels.
1318 	if (GetLevel() == 0) {
1319 		Output::Debug("Actor {}: Special handling for level 0", GetId());
1320 	} else if (GetLevel() < 0) {
1321 		Output::Warning("Actor {}: Invalid level {}, changed to 1", GetId(), GetLevel());
1322 		SetLevel(1);
1323 	} else if (GetLevel() > GetMaxLevel()) {
1324 		Output::Warning("Actor {}: Invalid level {}, changed to {}", GetId(), GetLevel(), GetMaxLevel());
1325 		SetLevel(GetMaxLevel());
1326 	}
1327 }
1328 
GetWeapon() const1329 const lcf::rpg::Item* Game_Actor::GetWeapon() const {
1330 	auto* weapon = GetEquipment(lcf::rpg::Item::Type_weapon);
1331 	if (weapon && weapon->type == lcf::rpg::Item::Type_weapon) {
1332 		return weapon;
1333 	}
1334 	return nullptr;
1335 }
1336 
Get2ndWeapon() const1337 const lcf::rpg::Item* Game_Actor::Get2ndWeapon() const {
1338 	// Checking of HasTwoWeapons() not neccessary. If true, the
1339 	// item equipped in this slot will never be a weapon from
1340 	// legitimate means.
1341 	auto* weapon = GetEquipment(lcf::rpg::Item::Type_shield);
1342 	if (weapon && weapon->type == lcf::rpg::Item::Type_weapon) {
1343 		return weapon;
1344 	}
1345 	return nullptr;
1346 }
1347 
GetShield() const1348 const lcf::rpg::Item* Game_Actor::GetShield() const {
1349 	auto* shield = GetEquipment(lcf::rpg::Item::Type_shield);
1350 	if (shield && shield->type == lcf::rpg::Item::Type_shield) {
1351 		return shield;
1352 	}
1353 	return nullptr;
1354 }
1355 
GetArmor() const1356 const lcf::rpg::Item* Game_Actor::GetArmor() const {
1357 	auto* armor = GetEquipment(lcf::rpg::Item::Type_armor);
1358 	if (armor && armor->type == lcf::rpg::Item::Type_armor) {
1359 		return armor;
1360 	}
1361 	return nullptr;
1362 }
1363 
GetHelmet() const1364 const lcf::rpg::Item* Game_Actor::GetHelmet() const {
1365 	auto* helmet = GetEquipment(lcf::rpg::Item::Type_helmet);
1366 	if (helmet && helmet->type == lcf::rpg::Item::Type_helmet) {
1367 		return helmet;
1368 	}
1369 	return nullptr;
1370 }
1371 
GetAccessory() const1372 const lcf::rpg::Item* Game_Actor::GetAccessory() const {
1373 	auto* accessory = GetEquipment(lcf::rpg::Item::Type_accessory);
1374 	if (accessory && accessory->type == lcf::rpg::Item::Type_accessory) {
1375 		return accessory;
1376 	}
1377 	return nullptr;
1378 }
1379 
HasPreemptiveAttack(Weapon weapon) const1380 bool Game_Actor::HasPreemptiveAttack(Weapon weapon) const {
1381 	bool rc = false;
1382 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { rc |= item.preemptive; }, weapon);
1383 	return rc;
1384 }
1385 
GetNumberOfAttacks(Weapon weapon) const1386 int Game_Actor::GetNumberOfAttacks(Weapon weapon) const {
1387 	int hits = 1;
1388 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { hits = std::max(hits, Algo::GetNumberOfAttacks(GetId(), item)); }, weapon);
1389 	return hits;
1390 }
1391 
HasAttackAll(Weapon weapon) const1392 bool Game_Actor::HasAttackAll(Weapon weapon) const {
1393 	bool rc = false;
1394 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { rc |= item.attack_all; }, weapon);
1395 	return rc;
1396 }
1397 
AttackIgnoresEvasion(Weapon weapon) const1398 bool Game_Actor::AttackIgnoresEvasion(Weapon weapon) const {
1399 	bool rc = false;
1400 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { rc |= item.ignore_evasion; }, weapon);
1401 	return rc;
1402 }
1403 
PreventsCritical() const1404 bool Game_Actor::PreventsCritical() const {
1405 	bool rc = false;
1406 	ForEachEquipment<false, true>(GetWholeEquipment(), [&](auto& item) { rc |= item.prevent_critical; });
1407 	return rc;
1408 }
1409 
PreventsTerrainDamage() const1410 bool Game_Actor::PreventsTerrainDamage() const {
1411 	bool rc = false;
1412 	ForEachEquipment<false, true>(GetWholeEquipment(), [&](auto& item) { rc |= item.no_terrain_damage; });
1413 	return rc;
1414 }
1415 
HasPhysicalEvasionUp() const1416 bool Game_Actor::HasPhysicalEvasionUp() const {
1417 	bool rc = false;
1418 	ForEachEquipment<false, true>(GetWholeEquipment(), [&](auto& item) { rc |= item.raise_evasion; });
1419 	return rc;
1420 }
1421 
HasHalfSpCost() const1422 bool Game_Actor::HasHalfSpCost() const {
1423 	bool rc = false;
1424 	ForEachEquipment<false, true>(GetWholeEquipment(), [&](auto& item) { rc |= item.half_sp_cost; });
1425 	return rc;
1426 }
1427 
CalculateWeaponSpCost(Weapon weapon) const1428 int Game_Actor::CalculateWeaponSpCost(Weapon weapon) const {
1429 	int cost = 0;
1430 	ForEachEquipment<true, false>(GetWholeEquipment(), [&](auto& item) { cost += item.sp_cost; }, weapon);
1431 	if (HasHalfSpCost()) {
1432 		cost = (cost + 1) / 2;
1433 	}
1434 
1435 	return cost;
1436 }
1437 
AdjustEquipmentStates(const lcf::rpg::Item * item,bool add,bool allow_battle_states)1438 void Game_Actor::AdjustEquipmentStates(const lcf::rpg::Item* item, bool add, bool allow_battle_states) {
1439 	// All states inflicted by new armor get inflicted.
1440 	if (Player::IsRPG2k3()
1441 			&& item
1442 			&& IsArmorType(item)
1443 			&& item->reverse_state_effect)
1444 	{
1445 		auto& states = item->state_set;
1446 		for (int i = 0; i < (int)states.size(); ++i) {
1447 			if (states[i]) {
1448 				if (add) {
1449 					AddState(i + 1, allow_battle_states);
1450 				} else {
1451 					RemoveState(i + 1, false);
1452 				}
1453 			}
1454 		}
1455 	}
1456 }
1457 
1458 
ResetEquipmentStates(bool allow_battle_states)1459 void Game_Actor::ResetEquipmentStates(bool allow_battle_states) {
1460 	AdjustEquipmentStates(GetShield(), true, allow_battle_states);
1461 	AdjustEquipmentStates(GetArmor(), true, allow_battle_states);
1462 	AdjustEquipmentStates(GetHelmet(), true, allow_battle_states);
1463 	AdjustEquipmentStates(GetAccessory(), true, allow_battle_states);
1464 }
1465 
GetPermanentStates() const1466 PermanentStates Game_Actor::GetPermanentStates() const {
1467 	PermanentStates ps;
1468 	if (!Player::IsRPG2k3()) {
1469 		return ps;
1470 	}
1471 
1472 	auto addEquip = [&](const lcf::rpg::Item* item) {
1473 		if (!item || !IsArmorType(item) || !item->reverse_state_effect) {
1474 			return;
1475 		}
1476 		auto& states = item->state_set;
1477 		// Invalid states in armor already reported earlier in
1478 		// calls to AdjustEquipmentStates.
1479 		int num_states = std::min<int>(states.size(), lcf::Data::states.size());
1480 		for (int i = 0; i < num_states; ++i) {
1481 			if (states[i]) {
1482 				ps.Add(i + 1);
1483 			}
1484 		}
1485 	};
1486 
1487 	addEquip(GetShield());
1488 	addEquip(GetArmor());
1489 	addEquip(GetHelmet());
1490 	addEquip(GetAccessory());
1491 
1492 	return ps;
1493 }
1494 
IsInParty() const1495 bool Game_Actor::IsInParty() const {
1496 	return Main_Data::game_party->IsActorInParty(GetId());
1497 }
1498 
GetWeapons(Game_Battler::Weapon weapon) const1499 std::array<const lcf::rpg::Item*, 2> Game_Actor::GetWeapons(Game_Battler::Weapon weapon) const {
1500 	std::array<const lcf::rpg::Item*, 2> w = {{}};
1501 	int i = 0;
1502 	if (weapon == Game_Battler::WeaponPrimary || weapon == Game_Battler::WeaponAll) {
1503 		w[i] = GetWeapon();
1504 		if (w[i]) { ++i; }
1505 	}
1506 	if (weapon == Game_Battler::WeaponSecondary || weapon == Game_Battler::WeaponAll) {
1507 		w[i] = Get2ndWeapon();
1508 	}
1509 	return w;
1510 }
1511 
1512 
UpdateBattle()1513 void Game_Actor::UpdateBattle() {
1514 	Game_Battler::UpdateBattle();
1515 	auto* sprite = GetActorBattleSprite();
1516 	if (sprite) {
1517 		sprite->Update();
1518 	}
1519 	auto* weapon = Game_Battler::GetWeaponSprite();
1520 	if (weapon) {
1521 		weapon->Update();
1522 	}
1523 }
1524