Alchemy
Loading...
Searching...
No Matches

targeting.c

Go to the documentation of this file.
1/// @file
2/// The targets an enemy's or a confused unit's ability picks.
3/// Part of battle.c, the battle object, compiled only through it.
4
6
7// Native-only view retained for the measured target-picker form below.
8#ifndef UBFIX
9/// BattleWork's unit lists, read as one array of halfwords.
10struct SlotArray {
11 s16 items[64]; ///< BattleWork from its start; each list at its offset / 2
12};
13#endif
14
15/// The list place codes the target pickers return: the party's places
16/// 0x100 | place, the enemies' 0x180 | place (Battle_FindTaggedSlotByValue).
17#define PLACE_PARTY 0x100
18#define PLACE_ENEMY 0x180
19
20/// Picks a random place: with requireLivingUnit, a party member still
21/// standing; otherwise any enemy place in use. Returns its place code, or 0
22/// when there is none.
23/// Called by BattleCommand_SelectAutomatic when BattleTarget_SelectForAction
24/// finds no one listed on the side (-2). Takes one Random16 roll when any
25/// position qualifies.
27
28 u16 positions[6];
29 s32 count;
30
31 // UB: the native view treats distinct BattleWork members as one array.
32 // Walking the actual unit list changes EN from 0xc2 to 0xa8 bytes.
33#ifdef UBFIX
34 s16 *unitIds;
35 s32 unitId;
36 s32 place;
37 s32 sideTag;
38
39 count = 0;
40 unitIds = requireLivingUnit ? gBattleWork->partyUnits : gBattleWork->enemyIds;
41 sideTag = requireLivingUnit ? PLACE_PARTY : PLACE_ENEMY;
42 for (place = 0; unitIds[place] != BATTLE_UNIT_LIST_END; place++) {
43 unitId = unitIds[place];
44 if (unitId == BATTLE_UNIT_REMOVED)
45 continue;
46 if (requireLivingUnit && UnitGetFar(unitId)->hp == 0)
47 continue;
48 positions[count++] = place | sideTag;
49 }
50#else
51 struct SlotArray *order;
52 s16 *entry;
53 u16 *cursor;
54 s32 value;
55 s32 index;
56 s32 slot;
57 s32 tail;
58 s32 offset;
59
60 count = 0;
61 order = (struct SlotArray *)gBattleWork;
62
63 // The party's places whose unit is still standing; then, in the else
64 // branch, the enemies' places in use, whether standing or not. Each walk
65 // indexes order from its list's offset in BattleWork, in halfwords.
66 if (requireLivingUnit != 0) {
67 /* FAKEMATCH(one-pass): a loop left by its closing break; without it, 14
68 * of 89 instructions differ in tbs-en. */
69 for (;;) {
70 index = 0;
71 slot = (u32)&((struct BattleWork *)0)->partyUnits / sizeof(s16);
72 if (order->items[slot] != BATTLE_UNIT_LIST_END) {
73 entry = order->items;
74 do {
75 value = entry[slot];
76 if (value != BATTLE_UNIT_REMOVED) {
77 if (UnitGetFar(value)->hp != 0) {
78 positions[count] = index | PLACE_PARTY;
79 count++;
80 }
81 }
82 slot++;
83 index++;
84 } while (entry[slot] != BATTLE_UNIT_LIST_END);
85 }
86 break;
87 }
88 } else {
89 index = 0;
90 slot = (u32)&((struct BattleWork *)0)->enemyIds / sizeof(s16) - 1;
91 tail = (u32)&((struct BattleWork *)0)->enemyIds / sizeof(s16) - 1;
92 offset = tail * 2;
93 entry = (order->items + 1);
94 if (*(s16 *)((char *)entry + offset) != BATTLE_UNIT_LIST_END) {
95 cursor = entry;
96 do {
97 if ((s16)cursor[slot] != BATTLE_UNIT_REMOVED) {
98 positions[count] = index | PLACE_ENEMY;
99 count++;
100 }
101 slot++;
102 tail++;
103 index++;
104 } while (entry[tail] != BATTLE_UNIT_LIST_END);
105 }
106 }
107#endif
108
109 if (count == 0)
110 return 0;
111 // A 16-bit roll times the count, over 65536: an index below count.
112 return positions[(Random16() * count) >> 16];
113}
114
115/// Counts the ailments of unit that a partial cure removes (delusion,
116/// confusion, charm, sleep, Psynergy seal, the Grim Reaper's curse).
117#define COUNT_PARTIAL_CURES(unit, count) \
118 { \
119 if ((unit)->delusion != 0) \
120 (count)++; \
121 if ((u8)(unit)->confusion != 0) \
122 (count)++; \
123 if ((unit)->charm != 0) \
124 (count)++; \
125 if ((unit)->sleep != 0) \
126 (count)++; \
127 if ((unit)->psySeal != 0) \
128 (count)++; \
129 if ((unit)->grimReaper != 0) \
130 (count)++; \
131 }
132
133/// Counts the ailments a full cure removes: those, haunting and poison.
134#define COUNT_ALL_CURES(unit, count) \
135 { \
136 COUNT_PARTIAL_CURES((unit), (count)); \
137 if ((unit)->haunt != 0) \
138 (count)++; \
139 if ((unit)->poison != 0) \
140 (count)++; \
141 }
142
143/// Counts unit's raised stats: Attack, Defense, Resistance and each element.
144#define COUNT_POSITIVE_MODIFIERS(unit, count) \
145 { \
146 if ((unit)->attackStage > 0) \
147 (count)++; \
148 if ((unit)->defenseStage > 0) \
149 (count)++; \
150 if ((unit)->resistanceStage > 0) \
151 (count)++; \
152 if ((unit)->elementLevels[0] > 0) \
153 (count)++; \
154 if ((unit)->elementLevels[1] > 0) \
155 (count)++; \
156 if ((unit)->elementLevels[2] > 0) \
157 (count)++; \
158 if ((unit)->elementLevels[3] > 0) \
159 (count)++; \
160 }
161
162/// How worthwhile an enemy's ability is against unit, in applies (0 for
163/// pointless): a stat change that can still move (one more when it is about
164/// to wear off), a cure or a heal the unit needs, an ailment it lacks, a
165/// revival of a downed unit (100). Failing its added effect, the ability's
166/// damage or healing must have something to work on. kindIndex receives the
167/// ability's kind less one.
168#define CHECK_EFFECT_TARGET(unit, action, applies, kindIndex) \
169 { \
170 (applies) = 0; \
171 switch ((action)->effect) { \
172 case ABILITY_EFFECT_ATTACK_UP_2: \
173 case ABILITY_EFFECT_ATTACK_UP: \
174 if ((unit)->attackStage + 1 <= 4) \
175 (applies) = 1; \
176 if ((unit)->attackTurns == 1) \
177 (applies)++; \
178 break; \
179 case ABILITY_EFFECT_ATTACK_DOWN_2: \
180 case ABILITY_EFFECT_ATTACK_DOWN: \
181 if ((unit)->attackStage - 1 >= -4) \
182 (applies) = 1; \
183 if ((unit)->attackTurns == 1) \
184 (applies)++; \
185 break; \
186 case ABILITY_EFFECT_DEFENSE_UP_2: \
187 case ABILITY_EFFECT_DEFENSE_UP: \
188 if ((unit)->defenseStage + 1 <= 4) \
189 (applies) = 1; \
190 if ((unit)->defenseTurns == 1) \
191 (applies)++; \
192 break; \
193 case ABILITY_EFFECT_DEFENSE_DOWN_2: \
194 case ABILITY_EFFECT_DEFENSE_DOWN: \
195 if ((unit)->defenseStage - 1 >= -4) \
196 (applies) = 1; \
197 if ((unit)->defenseTurns == 1) \
198 (applies)++; \
199 break; \
200 case ABILITY_EFFECT_RESISTANCE_UP_2: \
201 case ABILITY_EFFECT_RESISTANCE_UP: \
202 if ((unit)->resistanceStage + 1 <= 4) \
203 (applies) = 1; \
204 if ((unit)->resistanceTurns == 1) \
205 (applies)++; \
206 break; \
207 case ABILITY_EFFECT_RESISTANCE_DOWN_2: \
208 case ABILITY_EFFECT_RESISTANCE_DOWN: \
209 if ((unit)->resistanceStage - 1 >= -4) \
210 (applies) = 1; \
211 if ((unit)->resistanceTurns == 1) \
212 (applies)++; \
213 break; \
214 case ABILITY_EFFECT_CURE_POISON: \
215 if ((unit)->poison != 0) \
216 (applies) = 1; \
217 break; \
218 case ABILITY_EFFECT_RESTORE: \
219 COUNT_PARTIAL_CURES((unit), (applies)); \
220 break; \
221 case ABILITY_EFFECT_HEAL_HP_60: \
222 case ABILITY_EFFECT_HEAL_HP_30: \
223 if ((unit)->hp < (unit)->maxHp) \
224 (applies) = 1; \
225 break; \
226 case ABILITY_EFFECT_BREAK: \
227 COUNT_POSITIVE_MODIFIERS((unit), (applies)); \
228 break; \
229 case ABILITY_EFFECT_CURE_ALL: \
230 COUNT_ALL_CURES((unit), (applies)); \
231 break; \
232 case ABILITY_EFFECT_CURSE: \
233 if ((unit)->grimReaper == 0) \
234 (applies) = 1; \
235 break; \
236 case ABILITY_EFFECT_POISON: \
237 if ((unit)->poison == 0) \
238 (applies) = 1; \
239 break; \
240 case ABILITY_EFFECT_DEADLY_POISON: \
241 if ((unit)->poison <= 1) \
242 (applies) = 1; \
243 break; \
244 case ABILITY_EFFECT_STUN: \
245 if ((unit)->stun == 0) \
246 (applies) = 1; \
247 break; \
248 case ABILITY_EFFECT_SLEEP: \
249 if ((unit)->sleep == 0) \
250 (applies) = 1; \
251 break; \
252 case ABILITY_EFFECT_HAUNT: \
253 if ((unit)->haunt == 0) \
254 (applies) = 1; \
255 break; \
256 case ABILITY_EFFECT_REVIVE: \
257 case ABILITY_EFFECT_REVIVE_HALF: \
258 case ABILITY_EFFECT_REVIVE_80: \
259 if ((unit)->hp == 0) \
260 (applies) = 100; \
261 break; \
262 case 0: \
263 case 1: \
264 case 2: \
265 break; \
266 default: \
267 (applies) = 1; \
268 break; \
269 } \
270 if ((unit)->hp == 0 && Battle_IsReviveEffectFar((action)->effect) == 0) \
271 (applies) = 0; \
272 if ((applies) == 0) { \
273 (kindIndex) = ((action)->flags & ABILITY_KIND_MASK) - 1; \
274 switch (kindIndex) { \
275 case ABILITY_KIND_ATTACK - 1: \
276 case ABILITY_KIND_ATTACK_MULTIPLIED - 1: \
277 if ((unit)->hp != 0) \
278 (applies)++; \
279 break; \
280 case ABILITY_KIND_SUMMON - 1: \
281 case 9 - 1: \
282 if ((action)->power != 0 && (unit)->hp != 0) \
283 (applies)++; \
284 break; \
285 case ABILITY_KIND_PP_DAMAGE - 1: \
286 if ((unit)->pp != 0) \
287 (applies)++; \
288 break; \
289 case ABILITY_KIND_HEAL_HP - 1: \
290 if ((unit)->hp != 0 && (unit)->hp < (unit)->maxHp) \
291 (applies)++; \
292 break; \
293 case ABILITY_KIND_EFFECT - 1: \
294 case ABILITY_KIND_DAMAGE - 1: \
295 case ABILITY_KIND_UNLEASHED_DAMAGE - 1: \
296 if ((action)->power != 0 && (unit)->hp != 0) \
297 (applies)++; \
298 break; \
299 } \
300 } \
301 }
302
303// EnemyDefinition.targetStrategy: a single-target attacker favours the
304// target with the most HP left, the most maximum HP, or no one.
305#define STRATEGY_MOST_HP 0
306#define STRATEGY_MOST_MAX_HP 1
307#define STRATEGY_RANDOM 2
308
309/// Picks the target of an enemy's ability (or a confused unit's), as a place
310/// code: among the units of the side it may target, those it would affect
311/// (CHECK_EFFECT_TARGET), at random; a single-target attack by an enemy with
312/// a strategy favours the strongest, the first of the sorted targets 6 times
313/// in 11, 15 or 18, the next 5 times, and so on. Returns -2 when the side
314/// has no one, -1 when the ability would affect no one.
315/// Called by BattleCommand_SelectAutomatic for an ability aimed at allies,
316/// the user or foes. Takes Random16 rolls to choose among eligible targets.
318 u16 orderPositions[6];
319 s32 unitIds[6];
320 s32 targetPositions[6];
321 struct BattleWork *turnOrder;
322 struct BattleUnit *unit;
323 struct BattleUnit *nextUnit;
324 struct BattleUnit *thisUnit;
325 s32 candidateCount;
326 s32 targetIndex;
327 s32 targetCount;
328 s32 innerIndex;
329 s32 selected;
330 s32 unitId;
331 s32 value;
332 s32 nextValue;
333 s32 temp;
334 s32 ownSide;
335 s32 kindIndex;
336 s32 applies;
337 u32 roll;
338 s16 *slot;
339
340 turnOrder = gBattleWork;
341 targetCount = 0;
342 candidateCount = 0;
343
344 // The candidates: every unit listed on the side the ability may target,
345 // downed or not (only the user, for TARGET_MODE_SELF), with its place code.
346 // Allies of a party unit and foes of an enemy are the party.
347 if (action->targetMode != TARGET_MODE_NONE) {
348 ownSide = 0;
349 if (action->targetMode == TARGET_MODE_ALLIES ||
350 action->targetMode == TARGET_MODE_SELF)
351 ownSide = 1;
352
353 if (actorId < 0 || actorId > 7) {
354 if (ownSide != 0)
355 goto scan_enemies;
356 } else if (ownSide == 0) {
357 goto scan_enemies;
358 }
359
360 targetIndex = 0;
361 while (turnOrder->partyUnits[targetIndex] != BATTLE_UNIT_LIST_END) {
362 slot = &turnOrder->partyUnits[targetIndex];
363 unitId = *slot;
364 if (unitId != BATTLE_UNIT_REMOVED) {
365 if (action->targetMode != TARGET_MODE_SELF || unitId == actorId) {
366 unitIds[candidateCount] = unitId;
367 orderPositions[candidateCount] = PLACE_PARTY;
368 orderPositions[candidateCount] |= targetIndex;
369 candidateCount++;
370 }
371 }
372 targetIndex++;
373 }
374 goto scan_complete;
375
376 scan_enemies:
377 targetIndex = 0;
378 while (turnOrder->enemyIds[targetIndex] != BATTLE_UNIT_LIST_END) {
379 unitId = turnOrder->enemyIds[targetIndex];
380 if (unitId != BATTLE_UNIT_REMOVED) {
381 if (action->targetMode != TARGET_MODE_SELF || unitId == actorId) {
382 unitIds[candidateCount] = unitId;
383 orderPositions[candidateCount] = PLACE_ENEMY;
384 orderPositions[candidateCount] |= targetIndex;
385 candidateCount++;
386 }
387 }
388 targetIndex++;
389 }
390 }
391
392scan_complete:
393
394 if (candidateCount == 0)
395 return -2;
396
397 // Keep the candidates the ability would affect, in place.
398 for (targetIndex = 0; targetIndex < candidateCount; targetIndex++) {
399 unit = UnitGetFar(unitIds[targetIndex]);
400 CHECK_EFFECT_TARGET(unit, action, applies, kindIndex);
401 if (applies != 0) {
402 unitIds[targetCount] = unitIds[targetIndex];
403 targetPositions[targetCount] = orderPositions[targetIndex];
404 targetCount++;
405 }
406 }
407
408 if (targetCount == 0)
409 return -1;
410
411 if (action->targetMode == TARGET_MODE_FOES && action->range == 1 &&
413 UnitGetFar(actorId)->classId))
414 ->targetStrategy != STRATEGY_RANDOM &&
415 (u32)((action->flags & ABILITY_KIND_MASK) - ABILITY_KIND_ATTACK) <= 2) {
416 // A single-target attack of kind 3 to 5 by an enemy with a strategy: sort
417 // the targets by HP left or by maximum HP, the most first. Each pass of
418 // the exchange sort starts one place later, so the order can come out
419 // wrong: HP 1, 2 and 3 end as 2, 3 and 1.
420 selected = -1;
421 for (targetIndex = 0; targetIndex < targetCount; targetIndex++) {
422 for (innerIndex = targetIndex; innerIndex < targetCount - 1;
423 innerIndex++) {
424 thisUnit = UnitGetFar(unitIds[innerIndex]);
425 nextUnit = UnitGetFar(unitIds[innerIndex + 1]);
427 UnitGetFar(actorId)->classId))
428 ->targetStrategy == STRATEGY_MOST_HP) {
429 value = thisUnit->hp;
430 nextValue = nextUnit->hp;
431 } else {
432 value = thisUnit->maxHp;
433 nextValue = nextUnit->maxHp;
434 }
435 if (value < nextValue) {
436 temp = unitIds[innerIndex];
437 unitIds[innerIndex] = unitIds[innerIndex + 1];
438 unitIds[innerIndex + 1] = temp;
439 temp = targetPositions[innerIndex];
440 targetPositions[innerIndex] = targetPositions[innerIndex + 1];
441 targetPositions[innerIndex + 1] = temp;
442 }
443 }
444 }
445
446 // Weighted toward the front: two targets 6 and 5 in 11, three 6, 5 and 4
447 // in 15, four 6, 5, 4 and 3 in 18. Five or more fall through to the even
448 // roll below.
449 switch (targetCount) {
450 case 1:
451 selected = 0;
452 break;
453 case 2:
454 roll = (11 * Random16()) >> 16;
455 selected = 0;
456 if (roll > 5)
457 selected = 1;
458 break;
459 case 3:
460 selected = (15 * Random16()) >> 16;
461 if (selected <= 5)
462 selected = 0;
463 else if (selected <= 10)
464 selected = 1;
465 else
466 selected = 2;
467 break;
468 case 4:
469 selected = (18 * Random16()) >> 16;
470 if (selected <= 5)
471 selected = 0;
472 else if (selected <= 10)
473 selected = 1;
474 else if (selected <= 14)
475 selected = 2;
476 else
477 selected = 3;
478 break;
479 }
480
481 if (selected >= 0)
482 return targetPositions[selected];
483 }
484 return targetPositions[(Random16() * targetCount) >> 16];
485}
What every part of the battle object shares: its includes, in their original order,...
#define TARGET_MODE_FOES
#define TARGET_MODE_NONE
#define TARGET_MODE_ALLIES
#define TARGET_MODE_SELF
@ BATTLE_UNIT_LIST_END
ends the list
@ BATTLE_UNIT_REMOVED
a place whose unit has left the battle
@ ABILITY_KIND_ATTACK
Attack vs Defense, plus the power.
Definition battle_unit.h:64
#define ABILITY_KIND_MASK
The low four bits of BattleAct.flags: how BattleTargetExec applies the ability's power.
Definition battle_unit.h:52
struct BattleWork * gBattleWork
struct BattleUnit * UnitGetFar(u32 owner)
const u8 * Enemy_GetDefinitionFar(s32 selector)
u32 Random16(void)
Advances the game's random number generator (the C library's classic linear congruential one),...
Definition math.c:10
An ability's definition, a row of gBattleAction_DefinitionTable: its targeting, kind,...
u8 range
the aimed unit and range - 1 either side, or ABILITY_RANGE_ALL
u8 targetMode
TARGET_MODE_NONE, _FOES, _ALLIES or _SELF.
u8 flags
bits: 7 usable in battle | 6 usable in the field | 5..4 clear in every row | 3..0 kind (ABILITY_KIND_...
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
s16 hp
current HP, 0 when downed
u8 classId
Who the unit is: a character's id (ISAAC to JENNA, below 8) or an enemy's id (BattleUnitSet).
s16 maxHp
0 to 1999 for a character, 9999 for an enemy (UnitCalc)
The battle's own work: its enemies, units and their objects, the turn's actions, the spoils,...
Definition battle_work.h:71
s16 partyUnits[7]
ends with BATTLE_UNIT_LIST_END
s16 enemyIds[BATTLE_ENEMY_CAPACITY+1]
ends with BATTLE_UNIT_LIST_END
An enemy's definition: its level and stats, items, element preset, the actions it chooses from and it...
Definition character.h:89
BattleWork's unit lists, read as one array of halfwords.
Definition targeting.c:10
s16 items[64]
BattleWork from its start; each list at its offset / 2.
Definition targeting.c:11
#define PLACE_ENEMY
Definition targeting.c:18
#define PLACE_PARTY
The list place codes the target pickers return: the party's places 0x100 | place, the enemies' 0x180 ...
Definition targeting.c:17
#define CHECK_EFFECT_TARGET(unit, action, applies, kindIndex)
How worthwhile an enemy's ability is against unit, in applies (0 for pointless): a stat change that c...
Definition targeting.c:168
#define STRATEGY_RANDOM
Definition targeting.c:307
#define STRATEGY_MOST_HP
Definition targeting.c:305
s32 BattleTarget_SelectForAction(s32 actorId, struct BattleAct *action)
Picks the target of an enemy's ability (or a confused unit's), as a place code: among the units of th...
Definition targeting.c:317
s32 BattleTarget_SelectRandomPosition(s32 requireLivingUnit)
Picks a random place: with requireLivingUnit, a party member still standing; otherwise any enemy plac...
Definition targeting.c:26
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13