1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 #include "modes/battle/battle_target.h"
12 
13 #include "modes/battle/battle.h"
14 
15 #include "common/global/actors/global_attack_point.h"
16 
17 using namespace vt_global;
18 using namespace vt_utils;
19 using namespace vt_system;
20 
21 namespace vt_battle
22 {
23 
24 namespace private_battle
25 {
26 
BattleTarget()27 BattleTarget::BattleTarget() :
28     _type(GLOBAL_TARGET_INVALID),
29     _attack_point(0),
30     _actor_target(nullptr)
31 {}
32 
BattleTarget(const BattleTarget & copy)33 BattleTarget::BattleTarget(const BattleTarget& copy)
34 {
35     if(this == &copy)  // Handle self-assignment case
36         return;
37 
38     _type = copy._type;
39     _attack_point = copy._attack_point;
40     _actor_target = copy._actor_target;
41 
42     _party_target.clear();
43     for (uint32_t i = 0; i < copy._party_target.size(); ++i) {
44         _party_target.push_back(copy._party_target[i]);
45     }
46 }
47 
operator =(const BattleTarget & copy)48 BattleTarget& BattleTarget::operator=(const BattleTarget& copy)
49 {
50     if(this == &copy)  // Handle self-assignment case
51         return *this;
52 
53     _type = copy._type;
54     _attack_point = copy._attack_point;
55     _actor_target = copy._actor_target;
56 
57     _party_target.clear();
58     for (uint32_t i = 0; i < copy._party_target.size(); ++i) {
59         _party_target.push_back(copy._party_target[i]);
60     }
61 
62     return *this;
63 }
64 
InvalidateTarget()65 void BattleTarget::InvalidateTarget()
66 {
67     _type = GLOBAL_TARGET_INVALID;
68     _attack_point = 0;
69     _actor_target = nullptr;
70     _party_target.clear();
71 }
72 
SetTarget(BattleActor * attacker,vt_global::GLOBAL_TARGET type,BattleActor * target,uint32_t attack_point)73 bool BattleTarget::SetTarget(BattleActor* attacker, vt_global::GLOBAL_TARGET type, BattleActor* target, uint32_t attack_point)
74 {
75     if((type <= GLOBAL_TARGET_INVALID) || (type >= GLOBAL_TARGET_TOTAL)) {
76         PRINT_WARNING << "invalid target type argument: " << type << std::endl;
77         return false;
78     }
79 
80     if (attacker == nullptr) {
81         PRINT_ERROR << "BattleTarget::SetTarget() called wirh nullptr attacker." << std::endl;
82         return false;
83     }
84 
85     InvalidateTarget();
86 
87     // Set the target party according to the target type
88     std::deque<BattleActor *>* party_target = nullptr;
89     switch(type) {
90     case GLOBAL_TARGET_SELF_POINT:
91     case GLOBAL_TARGET_ALLY_POINT:
92     case GLOBAL_TARGET_SELF:
93     case GLOBAL_TARGET_ALLY:
94     case GLOBAL_TARGET_ALLY_EVEN_DEAD:
95     case GLOBAL_TARGET_DEAD_ALLY_ONLY:
96     case GLOBAL_TARGET_ALL_ALLIES:
97         if(attacker->IsEnemy())
98             party_target = &BattleMode::CurrentInstance()->GetEnemyParty();
99         else
100             party_target = &BattleMode::CurrentInstance()->GetCharacterParty();
101         break;
102 
103     case GLOBAL_TARGET_FOE_POINT:
104     case GLOBAL_TARGET_FOE:
105     case GLOBAL_TARGET_ALL_FOES:
106         if(attacker->IsEnemy())
107             party_target = &BattleMode::CurrentInstance()->GetCharacterParty();
108         else
109             party_target = &BattleMode::CurrentInstance()->GetEnemyParty();
110         break;
111 
112     default:
113         // Shouldn't happen
114         PRINT_WARNING << "Invalid target type argument: " << type << std::endl;
115         return false;
116         break;
117     }
118 
119     // Check whether the actor is actually part of the party and fix this if needed.
120     if (target && std::find(party_target->begin(), party_target->end(), target) == party_target->end())
121         target = party_target->at(0);
122 
123     if (target != nullptr)
124         _actor_target = target;
125     else
126         _actor_target = party_target->at(0);
127 
128     _type = type;
129 
130     _party_target.clear();
131     for (size_t i = 0; i < party_target->size(); ++i) {
132         if (type != GLOBAL_TARGET_ALL_FOES) {
133             _party_target.push_back(party_target->at(i));
134         } else {
135             if (party_target->at(i)->CanFight()) {
136                 _party_target.push_back(party_target->at(i));
137             }
138         }
139     }
140 
141     _attack_point = attack_point;
142     if (_attack_point >= _actor_target->GetAttackPoints().size())
143         _attack_point = 0;
144 
145     // If the target is not a party and not the user themselves, select the first valid actor
146     if (type != GLOBAL_TARGET_ALL_FOES && (!IsValid() && !SelectNextActor())) {
147         InvalidateTarget();
148 
149         PRINT_WARNING << "Could not find an initial actor that was a valid target" << std::endl;
150         return false;
151     }
152     return true;
153 }
154 
IsValid()155 bool BattleTarget::IsValid()
156 {
157     if (!_actor_target || _party_target.empty()) {
158         PRINT_WARNING << "No valid actor or party set: " << _type << std::endl;
159         return false;
160     }
161 
162     if(IsTargetPoint(_type)) {
163         if(_attack_point >= _actor_target->GetAttackPoints().size())
164             return false;
165     }
166 
167     // If we can't find the actor within the party, then it's bad.
168     if (std::find(_party_target.begin(), _party_target.end(), _actor_target) == _party_target.end())
169         return false;
170 
171     bool permit_dead_targets = (_type == GLOBAL_TARGET_ALLY_EVEN_DEAD || _type == GLOBAL_TARGET_DEAD_ALLY_ONLY);
172 
173     if (!_actor_target->IsAlive() || !_actor_target->CanFight() || _actor_target->GetHitPoints() == 0)
174         return permit_dead_targets;
175 
176     return true;
177 }
178 
SelectNextPoint(bool direction)179 bool BattleTarget::SelectNextPoint(bool direction)
180 {
181     if(IsTargetPoint(_type) == false) {
182         IF_PRINT_WARNING(BATTLE_DEBUG) << "invalid target type: " << _type << std::endl;
183         return false;
184     }
185     if(_actor_target == nullptr || _party_target.empty()) {
186         IF_PRINT_WARNING(BATTLE_DEBUG) << "No valid target set" << std::endl;
187         return false;
188     }
189 
190     // First check for the case where we need to select a new actor first
191     if(!IsValid()) {
192         _attack_point = 0;
193         return SelectNextActor();
194     }
195 
196     // If the actor has only a single attack point, there's no way to select another attack point
197     uint32_t num_points = _actor_target->GetAttackPoints().size();
198     if(num_points == 1)
199         return true;
200 
201     if(direction) {
202         ++_attack_point;
203         if(_attack_point >= num_points)
204             _attack_point = 0;
205     } else {
206         if(_attack_point == 0)
207             _attack_point = num_points - 1;
208         else
209             --_attack_point;
210     }
211     return true;
212 }
213 
SelectNextActor(bool direction)214 bool BattleTarget::SelectNextActor(bool direction)
215 {
216     if(!IsTargetPoint(_type) && !IsTargetActor(_type)) {
217         IF_PRINT_WARNING(BATTLE_DEBUG) << "Invalid target type: " << _type << std::endl;
218         return false;
219     }
220 
221     // Check the target party for early exit conditions
222     if(_party_target.empty()) {
223         IF_PRINT_WARNING(BATTLE_DEBUG) << "Actor target's party was empty" << std::endl;
224         return false;
225     }
226     if(_party_target.size() == 1) {
227         return false; // No more actors to select from in the party
228     }
229 
230     // The target died in between events. Let's reset it.
231     if(!_actor_target) {
232         _actor_target = _party_target.at(0);
233     }
234 
235     // Determine the index of the current actor in the target party
236     uint32_t original_target_index = 0xFFFFFFFF; // Initially set to an impossibly high index for error checking
237     for(uint32_t i = 0; i < _party_target.size(); ++i) {
238         if(_party_target.at(i) == _actor_target) {
239             original_target_index = i;
240             break;
241         }
242     }
243     if(original_target_index == 0xFFFFFFFF) {
244         IF_PRINT_WARNING(BATTLE_DEBUG) << "actor target was not found in party" << std::endl;
245         return false;
246     }
247 
248     // Starting from the index of the original actor, select the next available actor
249     BattleActor* original_actor = _actor_target;
250     uint32_t new_target_index = original_target_index;
251     while(true) {
252         // Increment or decrement the target index based on the direction argument
253         if(direction) {
254             new_target_index = (new_target_index >= _party_target.size() - 1) ? 0 : new_target_index + 1;
255         } else {
256             new_target_index = (new_target_index == 0) ? _party_target.size() - 1 : new_target_index - 1;
257         }
258 
259         // If we've reached the original target index then we were unable to select another actor target
260         if(new_target_index == original_target_index) {
261             _actor_target = original_actor;
262             return false;
263         }
264 
265         // Set the new actor target and if required, ascertain the new target's validity. If the new target
266         // must be valid and this new actor is not, the loop will continue and will try again with the next actor
267         _actor_target = _party_target.at(new_target_index);
268 
269         // Since we're changing the target, we reinit the attack point to the first one,
270         // as the new target may have less attack points than the latest one.
271         ReinitAttackPoint();
272 
273         if (IsValid())
274             return true;
275     }
276 }
277 
GetPartyActor(uint32_t index)278 BattleActor* BattleTarget::GetPartyActor(uint32_t index)
279 {
280     if (index >= _party_target.size())
281         return nullptr;
282 
283     return _party_target.at(index);
284 }
285 
GetName()286 ustring BattleTarget::GetName()
287 {
288     switch(_type) {
289     default:
290         return UTranslate("[Invalid Target]");
291 
292     case GLOBAL_TARGET_SELF_POINT:
293     case GLOBAL_TARGET_ALLY_POINT:
294     case GLOBAL_TARGET_FOE_POINT:
295         return (_actor_target->GetName() + UTranslate(" — ") + (_actor_target->GetAttackPoints()).at(_attack_point)->GetName());
296 
297     case GLOBAL_TARGET_SELF:
298     case GLOBAL_TARGET_ALLY:
299     case GLOBAL_TARGET_ALLY_EVEN_DEAD:
300     case GLOBAL_TARGET_DEAD_ALLY_ONLY:
301     case GLOBAL_TARGET_FOE:
302         return _actor_target->GetName();
303 
304     case GLOBAL_TARGET_ALL_ALLIES:
305         return UTranslate("All Allies");
306 
307     case GLOBAL_TARGET_ALL_FOES:
308         return UTranslate("All Enemies");
309     }
310 }
311 
312 } // namespace private_battle
313 
314 } // namespace vt_battle
315