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