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 == ©) // 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 == ©) // 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