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 <array>
21 #include <cassert>
22 #include <cmath>
23 #include "system.h"
24 #include "game_party.h"
25 #include "game_actors.h"
26 #include "game_map.h"
27 #include "game_player.h"
28 #include "game_battle.h"
29 #include "game_targets.h"
30 #include "game_system.h"
31 #include "scene_battle.h"
32 #include <lcf/reader_util.h>
33 #include "output.h"
34 #include "algo.h"
35 
Game_Party()36 Game_Party::Game_Party() {
37 }
38 
SetupNewGame()39 void Game_Party::SetupNewGame() {
40 	Clear();
41 
42 	data.party = lcf::Data::system.party;
43 	RemoveInvalidData();
44 }
45 
SetupFromSave(lcf::rpg::SaveInventory save)46 void Game_Party::SetupFromSave(lcf::rpg::SaveInventory save) {
47 	data = std::move(save);
48 	RemoveInvalidData();
49 
50 	// Old versions of player didn't sort the inventory, this ensures inventory is sorted
51 	// as our Game_Party code relies on that. Items in RPG_RT are always sorted in the inventory.
52 	if (!std::is_sorted(data.item_ids.begin(), data.item_ids.end())) {
53 		Output::Debug("Loaded Save Game with unsorted inventory! Sorting ...");
54 		// Resort the inventory.
55 		struct ItemData { int id; int count; int usage; };
56 
57 		auto& ids = data.item_ids;
58 		auto& counts = data.item_counts;
59 		auto& usages = data.item_usage;
60 
61 		auto num_items = std::min(ids.size(), std::min(counts.size(), usages.size()));
62 		std::vector<ItemData> items;
63 		for (size_t i = 0; i < num_items; ++i) {
64 			items.push_back(ItemData{ids[i], counts[i], usages[i]});
65 		}
66 
67 		std::sort(items.begin(), items.end(), [](const ItemData& l, const ItemData& r) { return l.id < r.id; });
68 
69 		ids.clear();
70 		counts.clear();
71 		usages.clear();
72 
73 		for (auto& itd: items) {
74 			ids.push_back(itd.id);
75 			counts.push_back(itd.count);
76 			usages.push_back(itd.usage);
77 		}
78 	}
79 }
80 
operator [](const int index)81 Game_Actor& Game_Party::operator[] (const int index) {
82 	std::vector<Game_Actor*> actors = GetActors();
83 
84 	if (index < 0 || (size_t)index >= actors.size()) {
85 		assert(false && "Subscript out of range");
86 	}
87 
88 	return *actors[index];
89 }
90 
GetBattlerCount() const91 int Game_Party::GetBattlerCount() const {
92 	return (int)GetActors().size();
93 }
94 
GetVisibleBattlerCount() const95 int Game_Party::GetVisibleBattlerCount() const {
96 	int visible = 0;
97 	for (const auto& actor: GetActors()) {
98 		visible += !actor->IsHidden();
99 	}
100 	return visible;
101 }
102 
SetupBattleTest()103 void Game_Party::SetupBattleTest() {
104 	Clear();
105 
106 	for (auto& btdata : lcf::Data::system.battletest_data) {
107 		AddActor(btdata.actor_id);
108 		Game_Actor* actor = Main_Data::game_actors->GetActor(btdata.actor_id);
109 
110 		// Filter garbage btdata inserted by the editor
111 		// The upper 16 bit look like uninitialized data
112 		std::array<int, 5> ids = {{
113 			btdata.weapon_id & 0xFFFF,
114 			btdata.shield_id & 0xFFFF,
115 			btdata.armor_id & 0xFFFF,
116 			btdata.helmet_id & 0xFFFF,
117 			btdata.accessory_id & 0xFFFF }};
118 		std::replace_if(ids.begin(), ids.end(), [] (const int& item_id) {
119 			return lcf::ReaderUtil::GetElement(lcf::Data::items, item_id) == nullptr;
120 		}, 0);
121 
122 		actor->SetEquipment(lcf::rpg::Item::Type_weapon, ids[0]);
123 		actor->SetEquipment(lcf::rpg::Item::Type_shield, ids[1]);
124 		actor->SetEquipment(lcf::rpg::Item::Type_armor, ids[2]);
125 		actor->SetEquipment(lcf::rpg::Item::Type_helmet, ids[3]);
126 		actor->SetEquipment(lcf::rpg::Item::Type_accessory, ids[4]);
127 		actor->ChangeLevel(btdata.level, nullptr);
128 		actor->SetHp(actor->GetMaxHp());
129 		actor->SetSp(actor->GetMaxSp());
130 	}
131 }
132 
GetItems(std::vector<int> & item_list)133 void Game_Party::GetItems(std::vector<int>& item_list) {
134 	item_list.clear();
135 
136 	std::vector<int16_t>::iterator it;
137 	for (it = data.item_ids.begin(); it != data.item_ids.end(); ++it)
138 		item_list.push_back(*it);
139 }
140 
GetItemCount(int item_id) const141 int Game_Party::GetItemCount(int item_id) const {
142 	auto ip = GetItemIndex(item_id);
143 	return ip.second ? data.item_counts[ip.first] : 0;
144 }
145 
GetEquippedItemCount(int item_id) const146 int Game_Party::GetEquippedItemCount(int item_id) const {
147 	int number = 0;
148 	if (item_id > 0) {
149 		for (int i = 0; i < (int) data.party.size(); i++) {
150 			Game_Actor* actor = Main_Data::game_actors->GetActor(data.party[i]);
151 			number += actor->GetItemCount(item_id);
152 		}
153 	}
154 	return number;
155 }
156 
GetItemTotalCount(int item_id) const157 int Game_Party::GetItemTotalCount(int item_id) const {
158 	return GetItemCount(item_id) + GetEquippedItemCount(item_id);
159 }
160 
GetMaxItemCount(int item_id) const161 int Game_Party::GetMaxItemCount(int item_id) const {
162 	const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
163 	if (!item || item->easyrpg_max_count == -1) {
164 		return (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count);
165 	} else {
166 		return item->easyrpg_max_count;
167 	}
168 }
169 
GainGold(int n)170 void Game_Party::GainGold(int n) {
171 	data.gold = data.gold + n;
172 	data.gold = std::min<int32_t>(std::max<int32_t>(data.gold, 0), 999999);
173 }
174 
LoseGold(int n)175 void Game_Party::LoseGold(int n) {
176 	data.gold = data.gold - n;
177 	data.gold = std::min<int32_t>(std::max<int32_t>(data.gold, 0), 999999);
178 }
179 
AddItem(int item_id,int amount)180 void Game_Party::AddItem(int item_id, int amount) {
181 	if (item_id < 1 || item_id > (int) lcf::Data::items.size()) {
182 		Output::Debug("Can't add item to party. {} is not a valid item ID.", item_id);
183 		return;
184 	}
185 
186 	int item_limit = GetMaxItemCount(item_id);
187 
188 	auto ip = GetItemIndex(item_id);
189 	auto idx = ip.first;
190 	auto has = ip.second;
191 	if (!has) {
192 		if (amount > 0) {
193 			amount = std::min(amount, item_limit);
194 			data.item_ids.insert(data.item_ids.begin() + idx, (int16_t)item_id);
195 			data.item_counts.insert(data.item_counts.begin() + idx, (uint8_t)amount);
196 			data.item_usage.insert(data.item_usage.begin() + idx, 0);
197 		}
198 		return;
199 	}
200 
201 	int total_items = data.item_counts[idx] + amount;
202 
203 	if (total_items <= 0) {
204 		data.item_ids.erase(data.item_ids.begin() + idx);
205 		data.item_counts.erase(data.item_counts.begin() + idx);
206 		data.item_usage.erase(data.item_usage.begin() + idx);
207 		return;
208 	}
209 
210 	data.item_counts[idx] = (uint8_t)std::min(total_items, item_limit);
211 	// If the item was removed, the number of uses resets.
212 	// (Adding an item never changes the number of uses, even when
213 	// you already have x99 of them.)
214 	if (amount < 0) {
215 		data.item_usage[idx] = 0;
216 	}
217 }
218 
RemoveItem(int item_id,int amount)219 void Game_Party::RemoveItem(int item_id, int amount) {
220 	AddItem(item_id, -amount);
221 }
222 
ConsumeItemUse(int item_id)223 void Game_Party::ConsumeItemUse(int item_id) {
224 	const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
225 
226 	if (!item) {
227 		Output::Warning("ConsumeItemUse: Invalid item ID {}.", item_id);
228 		return;
229 	}
230 
231 	switch (item->type) {
232 		case lcf::rpg::Item::Type_normal:
233 		case lcf::rpg::Item::Type_weapon:
234 		case lcf::rpg::Item::Type_shield:
235 		case lcf::rpg::Item::Type_armor:
236 		case lcf::rpg::Item::Type_helmet:
237 		case lcf::rpg::Item::Type_accessory:
238 			return;
239 	}
240 
241 	if (item->uses == 0) {
242 		// Unlimited uses
243 		return;
244 	}
245 
246 	auto ip = GetItemIndex(item_id);
247 	auto idx = ip.first;
248 	auto has = ip.second;
249 
250 	if (!has) {
251 		return;
252 	}
253 
254 	data.item_usage[idx]++;
255 
256 	if (data.item_usage[idx] >= item->uses) {
257 		if (data.item_counts[idx] == 1) {
258 			// We just used up the last one
259 			data.item_ids.erase(data.item_ids.begin() + idx);
260 			data.item_counts.erase(data.item_counts.begin() + idx);
261 			data.item_usage.erase(data.item_usage.begin() + idx);
262 		} else {
263 			data.item_counts[idx]--;
264 			data.item_usage[idx] = 0;
265 		}
266 	}
267 }
268 
IsItemUsable(int item_id,const Game_Actor * target) const269 bool Game_Party::IsItemUsable(int item_id, const Game_Actor* target) const {
270 	if (target && !target->IsItemUsable(item_id)) {
271 		return false;
272 	}
273 
274 	const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
275 	if (!item) {
276 		Output::Warning("IsItemUsable: Invalid item ID {}", item_id);
277 		return false;
278 	}
279 
280 	const auto* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, item->skill_id);
281 	const bool in_battle = Game_Battle::IsBattleRunning();
282 
283 	if (item->use_skill) {
284 		// RPG_RT BUG: Does not check if skill is usable.
285 		return skill &&
286 			(in_battle
287 			 || skill->scope == lcf::rpg::Skill::Scope_self
288 			 || skill->scope == lcf::rpg::Skill::Scope_ally
289 			 || skill->scope == lcf::rpg::Skill::Scope_party);
290 	}
291 
292 	switch (item->type) {
293 		case lcf::rpg::Item::Type_medicine:
294 			return !in_battle || !item->occasion_field1;
295 		case lcf::rpg::Item::Type_material:
296 		case lcf::rpg::Item::Type_book:
297 			return !in_battle;
298 		case lcf::rpg::Item::Type_switch:
299 			return in_battle ? item->occasion_battle : item->occasion_field2;
300 		case lcf::rpg::Item::Type_special:
301 			if (skill && Algo::IsSkillUsable(*skill, false)) {
302 				// RPG_RT requires one actor in the party and alive who can use the item.
303 				// But only if the item invokes a normal or subskill. This check is
304 				// not performed for escape, teleport, or switch skills!
305 				if (!Algo::IsNormalOrSubskill(*skill)) {
306 					return true;
307 				} else {
308 					for (auto* actor: GetActors()) {
309 						if (actor->CanAct() && actor->IsItemUsable(item_id)) {
310 							return true;
311 						}
312 					}
313 				}
314 			}
315 			return false;
316 		default:
317 			break;
318 	}
319 
320 	return false;
321 }
322 
UseItem(int item_id,Game_Actor * target)323 bool Game_Party::UseItem(int item_id, Game_Actor* target) {
324 	bool was_used = false;
325 
326 	auto* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id);
327 	if (!item) {
328 		Output::Warning("UseItem: Can't use item with invalid ID {}", item_id);
329 		return false;
330 	}
331 
332 	bool do_skill = (item->type == lcf::rpg::Item::Type_special)
333 		|| (item->use_skill && (
334 				item->type == lcf::rpg::Item::Type_weapon
335 				|| item->type == lcf::rpg::Item::Type_shield
336 				|| item->type == lcf::rpg::Item::Type_armor
337 				|| item->type == lcf::rpg::Item::Type_helmet
338 				|| item->type == lcf::rpg::Item::Type_accessory
339 				)
340 				);
341 
342 	const lcf::rpg::Skill* skill = nullptr;
343 	if (do_skill) {
344 		skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, item->skill_id);
345 		if (skill == nullptr) {
346 			Output::Warning("UseItem: Can't use item {} skill with invalid ID {}", item->ID, item->skill_id);
347 			return false;
348 		}
349 	}
350 
351 	const Game_Actor* fixed_source = nullptr;
352 	if (skill && skill->scope != lcf::rpg::Skill::Scope_self) {
353 		fixed_source = GetHighestLeveledActorWhoCanUse(item);
354 		if (fixed_source == nullptr) {
355 			return false;
356 		}
357 	}
358 
359 	if (target) {
360 		const auto* source = fixed_source ? fixed_source : target;
361 		if (IsItemUsable(item_id, source)) {
362 			was_used = target->UseItem(item_id, source);
363 		}
364 	} else {
365 		for (auto* actor: GetActors()) {
366 			const auto* source = fixed_source ? fixed_source : actor;
367 			if (IsItemUsable(item_id, source)) {
368 				was_used |= actor->UseItem(item_id, source);
369 			}
370 		}
371 	}
372 
373 	if (was_used) {
374 		ConsumeItemUse(item_id);
375 	}
376 
377 	return was_used;
378 }
379 
IsSkillUsable(int skill_id,const Game_Actor * target,bool from_item) const380 bool Game_Party::IsSkillUsable(int skill_id, const Game_Actor* target, bool from_item) const {
381 	if (skill_id <= 0 || skill_id > (int)lcf::Data::skills.size()) {
382 		return false;
383 	}
384 
385 	if (target && !target->IsSkillUsable(skill_id)) {
386 		return false;
387 	}
388 
389 	const lcf::rpg::Skill* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, skill_id);
390 	if (!skill) {
391 		Output::Warning("IsSkillUsable: Can't use skill with invalid ID {}", skill_id);
392 		return false;
393 	}
394 
395 	if (skill->type == lcf::rpg::Skill::Type_escape) {
396 		return !Game_Battle::IsBattleRunning() && Main_Data::game_system->GetAllowEscape() && Main_Data::game_targets->HasEscapeTarget() && !Main_Data::game_player->IsFlying();
397 	} else if (skill->type == lcf::rpg::Skill::Type_teleport) {
398 		return !Game_Battle::IsBattleRunning() && Main_Data::game_system->GetAllowTeleport() && Main_Data::game_targets->HasTeleportTargets() && !Main_Data::game_player->IsFlying();
399 	} else if (Algo::IsNormalOrSubskill(*skill)) {
400 		int scope = skill->scope;
401 
402 		if (Game_Battle::IsBattleRunning()) {
403 			return true;
404 		}
405 
406 		// Self targeting skills can not cure states only (except if called by an item).
407 		// RPG_RT logic...
408 
409 		if (scope == lcf::rpg::Skill::Scope_self) {
410 			return from_item || skill->affect_hp || skill->affect_sp;
411 		}
412 
413 		if (scope == lcf::rpg::Skill::Scope_ally ||
414 			scope == lcf::rpg::Skill::Scope_party) {
415 
416 			if (from_item || skill->affect_hp || skill->affect_sp) {
417 				return true;
418 			}
419 			for (size_t i = 0; i < skill->state_effects.size(); ++i) {
420 				auto& state = lcf::Data::states[i];
421 				if (skill->state_effects[i] && state.type == lcf::rpg::State::Persistence_persists) {
422 					return true;
423 				}
424 			}
425 			return false;
426 		}
427 	} else if (skill->type == lcf::rpg::Skill::Type_switch) {
428 		if (Game_Battle::IsBattleRunning()) {
429 			return skill->occasion_battle;
430 		}
431 
432 		return skill->occasion_field;
433 	}
434 
435 	return false;
436 }
437 
UseSkill(int skill_id,Game_Actor * source,Game_Actor * target)438 bool Game_Party::UseSkill(int skill_id, Game_Actor* source, Game_Actor* target) {
439 	bool was_used = false;
440 
441 	if (target) {
442 		was_used = target->UseSkill(skill_id, source);
443 	}
444 	else {
445 		std::vector<Game_Actor*> actors = GetActors();
446 		std::vector<Game_Actor*>::iterator it;
447 		for (it = actors.begin(); it != actors.end(); ++it) {
448 			was_used |= (*it)->UseSkill(skill_id, source);
449 		}
450 	}
451 
452 	if (was_used) {
453 		source->SetSp(source->GetSp() - source->CalculateSkillCost(skill_id));
454 	}
455 
456 	return was_used;
457 }
458 
AddActor(int actor_id)459 void Game_Party::AddActor(int actor_id) {
460 	auto* actor = Main_Data::game_actors->GetActor(actor_id);
461 	if (!actor) {
462 		return;
463 	}
464 
465 	if (IsActorInParty(actor_id))
466 		return;
467 	if (data.party.size() >= 4)
468 		return;
469 	data.party.push_back((int16_t)actor_id);
470 	Main_Data::game_player->ResetGraphic();
471 
472 	auto scene = Scene::Find(Scene::Battle);
473 	if (scene) {
474 		scene->OnPartyChanged(actor, true);
475 	}
476 }
477 
RemoveActor(int actor_id)478 void Game_Party::RemoveActor(int actor_id) {
479 	if (!IsActorInParty(actor_id))
480 		return;
481 	data.party.erase(std::find(data.party.begin(), data.party.end(), actor_id));
482 	Main_Data::game_player->ResetGraphic();
483 
484 	auto* actor = Main_Data::game_actors->GetActor(actor_id);
485 	if (!actor) {
486 		return;
487 	}
488 
489 	auto scene = Scene::Find(Scene::Battle);
490 	if (scene) {
491 		scene->OnPartyChanged(actor, false);
492 	}
493 }
494 
Clear()495 void Game_Party::Clear() {
496 	data.party.clear();
497 }
498 
IsActorInParty(int actor_id)499 bool Game_Party::IsActorInParty(int actor_id) {
500 	return std::find(data.party.begin(), data.party.end(), actor_id) != data.party.end();
501 }
502 
GetActorPositionInParty(int actor_id)503 int Game_Party::GetActorPositionInParty(int actor_id) {
504 	std::vector<short>::iterator it = std::find(data.party.begin(), data.party.end(), actor_id);
505 
506 	return it != data.party.end() ? std::distance(data.party.begin(), it) : -1;
507 }
508 
GetActors() const509 std::vector<Game_Actor*> Game_Party::GetActors() const {
510 	std::vector<Game_Actor*> actors;
511 	std::vector<int16_t>::const_iterator it;
512 	for (it = data.party.begin(); it != data.party.end(); ++it)
513 		actors.push_back(Main_Data::game_actors->GetActor(*it));
514 	return actors;
515 }
516 
GetActor(int idx) const517 Game_Actor* Game_Party::GetActor(int idx) const {
518 	if (idx >= 0 && idx < static_cast<int>(data.party.size())) {
519 		return Main_Data::game_actors->GetActor(data.party[idx]);
520 	}
521 	return nullptr;
522 }
523 
ApplyDamage(int damage,bool lethal)524 void Game_Party::ApplyDamage(int damage, bool lethal) {
525 	if (damage <= 0) {
526 		return;
527 	}
528 
529 	for (auto* actor: GetActors()) {
530 		actor->ChangeHp(-damage, lethal);
531 	}
532 }
533 
SetTimer(int which,int seconds)534 void Game_Party::SetTimer(int which, int seconds) {
535 	switch (which) {
536 		case Timer1:
537 			data.timer1_frames = seconds * DEFAULT_FPS + (DEFAULT_FPS - 1);
538 			Game_Map::SetNeedRefresh(true);
539 			break;
540 		case Timer2:
541 			data.timer2_frames = seconds * DEFAULT_FPS + (DEFAULT_FPS -1);
542 			Game_Map::SetNeedRefresh(true);
543 			break;
544 	}
545 }
546 
StartTimer(int which,bool visible,bool battle)547 void Game_Party::StartTimer(int which, bool visible, bool battle) {
548 	switch (which) {
549 		case Timer1:
550 			data.timer1_active = true;
551 			data.timer1_visible = visible;
552 			data.timer1_battle = battle;
553 			break;
554 		case Timer2:
555 			data.timer2_active = true;
556 			data.timer2_visible = visible;
557 			data.timer2_battle = battle;
558 			break;
559 	}
560 }
561 
StopTimer(int which)562 void Game_Party::StopTimer(int which) {
563 	switch (which) {
564 		case Timer1:
565 			data.timer1_active = false;
566 			data.timer1_visible = false;
567 			break;
568 		case Timer2:
569 			data.timer2_active = false;
570 			data.timer2_visible = false;
571 			break;
572 	}
573 }
574 
UpdateTimers()575 void Game_Party::UpdateTimers() {
576 	const bool battle = Game_Battle::IsBattleRunning();
577 	bool seconds_changed = false;
578 
579 	if (data.timer1_active && (data.timer1_battle || !battle) && data.timer1_frames > 0) {
580 		data.timer1_frames = data.timer1_frames - 1;
581 
582 		const int seconds = data.timer1_frames / DEFAULT_FPS;
583 		const int mod_frames = data.timer1_frames % DEFAULT_FPS;
584 		seconds_changed |= (mod_frames == (DEFAULT_FPS - 1));
585 
586 		if (seconds == 0) {
587 			StopTimer(Timer1);
588 		}
589 	}
590 
591 	if (data.timer2_active && (data.timer2_battle || !battle) && data.timer2_frames > 0) {
592 		data.timer2_frames = data.timer2_frames - 1;
593 
594 		const int seconds = data.timer2_frames / DEFAULT_FPS;
595 		const int mod_frames = data.timer2_frames % DEFAULT_FPS;
596 		seconds_changed |= (mod_frames == (DEFAULT_FPS - 1));
597 
598 		if (seconds == 0) {
599 			StopTimer(Timer2);
600 		}
601 	}
602 
603 	if (seconds_changed) {
604 		Game_Map::SetNeedRefresh(true);
605 	}
606 }
607 
GetTimerSeconds(int which)608 int Game_Party::GetTimerSeconds(int which) {
609 	return GetTimerFrames(which) / DEFAULT_FPS;
610 }
611 
GetTimerFrames(int which)612 int Game_Party::GetTimerFrames(int which) {
613 	switch (which) {
614 		case Timer1:
615 			return data.timer1_frames;
616 		case Timer2:
617 			return data.timer2_frames;
618 		default:
619 			return 0;
620 	}
621 }
622 
GetTimerVisible(int which,bool in_battle)623 bool Game_Party::GetTimerVisible(int which, bool in_battle) {
624 	bool visible = false;
625 	bool battle = false;
626 	switch (which) {
627 		case Timer1:
628 			visible = data.timer1_visible;
629 			battle = data.timer1_battle;
630 			break;
631 		case Timer2:
632 			visible = data.timer2_visible;
633 			battle = data.timer2_battle;
634 			break;
635 	}
636 	return visible && (!in_battle || battle);
637 }
638 
GetAverageLevel()639 int Game_Party::GetAverageLevel() {
640 	int party_lvl = 0;
641 
642 	std::vector<Game_Actor*> actors = GetActors();
643 	std::vector<Game_Actor*>::iterator it;
644 
645 	if (actors.empty()) {
646 		return 0;
647 	}
648 
649 	for (it = actors.begin(); it != actors.end(); ++it) {
650 		party_lvl += (*it)->GetLevel();
651 	}
652 
653 	return party_lvl / (int)actors.size();
654 }
655 
GetFatigue()656 int Game_Party::GetFatigue() {
657 	const auto& actors = GetActors();
658 
659 	if (actors.empty()) {
660 		return 0;
661 	}
662 
663 	int hp = 0;
664 	int total_hp = 0;
665 	int sp = 0;
666 	int total_sp = 0;
667 	for (auto* a : actors) {
668 		hp += a->GetHp();
669 		total_hp += a->GetMaxHp();
670 		sp += a->GetSp();
671 		total_sp += a->GetMaxSp();
672 	}
673 
674 	// SP is always 33.3% of fatigue, which means a 0 SP actor is never above 66%
675 	if (total_sp == 0) {
676 		total_sp = 1;
677 	}
678 
679 	auto p = Utils::RoundTo<int>(100.0f * ((static_cast<double>(hp) / total_hp * 2.0 + static_cast<double>(sp) / total_sp) / 3.0f));
680 	return 100 - p;
681 }
682 
RemoveInvalidData()683 void Game_Party::RemoveInvalidData() {
684 	// Remove non existing actors
685 	std::vector<int16_t> temp_party;
686 	std::swap(temp_party, data.party);
687 	std::vector<int16_t>::iterator it;
688 	for (it = temp_party.begin(); it != temp_party.end(); ++it) {
689 		if (Main_Data::game_actors->ActorExists(*it)) {
690 			data.party.push_back(*it);
691 		} else {
692 			Output::Warning("Removing invalid party member {}", *it);
693 		}
694 	}
695 
696 	// Remove non existing items
697 	for (it = data.item_ids.begin(); it != data.item_ids.end(); ) {
698 		if (!lcf::ReaderUtil::GetElement(lcf::Data::items, *it)) {
699 			Output::Warning("Removing invalid item {} from party", *it);
700 			it = data.item_ids.erase(it);
701 		} else {
702 			++it;
703 		}
704 	}
705 }
706 
GetInflictedStates() const707 std::vector<int16_t> Game_Party::GetInflictedStates() const {
708 	std::vector<int16_t> states;
709 
710 	for (auto actor : GetActors()) {
711 		std::vector<int16_t> actor_states = actor->GetInflictedStates();
712 		states.insert(states.end(), actor_states.begin(), actor_states.end());
713 	}
714 
715 	if (!states.empty()) {
716 		std::sort(states.begin(), states.end());
717 		states.erase(std::unique(states.begin(), states.end()), states.end());
718 	}
719 	return states;
720 }
721 
ApplyStateDamage()722 bool Game_Party::ApplyStateDamage() {
723 	bool damage = false;
724 	std::vector<int16_t> states = GetInflictedStates();
725 
726 	const auto steps = GetSteps();
727 
728 	for (auto state_id : states) {
729 		lcf::rpg::State *state = lcf::ReaderUtil::GetElement(lcf::Data::states, state_id);
730 
731 		if (state->hp_change_map_steps > 0
732 				&& state->hp_change_map_val > 0
733 				&& ((steps % state->hp_change_map_steps) == 0)
734 				) {
735 			for (auto actor : GetActors()) {
736 				if (actor->HasState(state_id)) {
737 					if (state->hp_change_type == lcf::rpg::State::ChangeType_lose) {
738 						actor->ChangeHp(-state->hp_change_map_val, false);
739 						damage = true;
740 					}
741 					else if (state->hp_change_type == lcf::rpg::State::ChangeType_gain) {
742 						actor->ChangeHp(state->hp_change_map_val, false);
743 					}
744 				}
745 			}
746 		}
747 
748 		if (state->sp_change_map_steps > 0
749 				&& state->sp_change_map_val > 0
750 				&& ((steps % state->sp_change_map_steps) == 0)
751 		   ){
752 			for (auto actor : GetActors()) {
753 				if (actor->HasState(state_id)) {
754 					if (state->sp_change_type == lcf::rpg::State::ChangeType_lose) {
755 						actor->ChangeSp(-state->sp_change_map_val);
756 						damage = true;
757 					}
758 					else if (state->sp_change_type == lcf::rpg::State::ChangeType_gain) {
759 						actor->ChangeSp(state->sp_change_map_val);
760 					}
761 				}
762 			}
763 		}
764 	}
765 
766 	return damage;
767 }
768 
IsAnyControllable()769 bool Game_Party::IsAnyControllable() {
770 	for (auto& actor: GetActors()) {
771 		if (actor->IsControllable()) {
772 			return true;
773 		}
774 	}
775 	return false;
776 }
777 
GetHighestLeveledActorWhoCanUse(const lcf::rpg::Item * item) const778 Game_Actor* Game_Party::GetHighestLeveledActorWhoCanUse(const lcf::rpg::Item* item) const {
779 	Game_Actor* best = nullptr;
780 
781 	for (auto* actor : GetActors()) {
782 		if (actor->CanAct()
783 				&& actor->IsItemUsable(item->ID)
784 				&& (best == nullptr || best->GetLevel() < actor->GetLevel())) {
785 			best = actor;
786 		}
787 	}
788 	return best;
789 }
790 
GetItemIndex(int item_id) const791 std::pair<int,bool> Game_Party::GetItemIndex(int item_id) const {
792 	auto& ids = data.item_ids;
793 	auto iter = std::lower_bound(ids.begin(), ids.end(), item_id);
794 	return std::make_pair(iter - ids.begin(), (iter != ids.end() && *iter == item_id));
795 }
796