Alchemy
Loading...
Searching...
No Matches

target_exec.c

Go to the documentation of this file.
1/// @file
2/// An ability applied to one target, BattleTargetExec: damage, healing and
3/// its added effect.
4/// Part of battle.c, the battle object, compiled only through it.
5
7
8/// Returns 1 for an effect that still applies when the ability's damage has
9/// just knocked its target out: the drains, which give the damage they dealt
10/// back to the actor.
11/// Called by BattleTargetExec before the added effect.
12/// effect : the ability's effect (enum AbilityEffect)
14 switch (effect) {
19 return 1;
20 default:
21 return 0;
22 }
23}
24
25// The target's checks, effect and messages
26/// A stat stage changes the stat by an eighth of it (UnitCalc), from half at
27/// the lowest to one and a half at the highest.
28#define STAT_STAGE_MIN -4
29#define STAT_STAGE_MAX 4
30/// Zephyr's Agility stage, beyond the usual highest: twice the Agility.
31#define AGILITY_STAGE_DOUBLED 8
32/// A Resistance stage adds 20 to the resistance to every element (UnitCalc).
33#define RESISTANCE_PER_STAGE 20
34
35// The turns a stat change or a status lasts; each may end sooner, at the
36// rolls the end of every turn makes (BattleUnit_RollCounterEarlyEnd).
37#define STAT_CHANGE_TURNS 7
38#define AGILITY_CHANGE_TURNS 5
39#define STATUS_TURNS 7
40/// The Grim Reaper's countdown.
41#define CURSE_TURNS 7
42
43/// BattleUnit.psySeal: Bind's seal counts its seven turns in the low three
44/// bits, and may end sooner; Luff's counts down 8 a turn, two turns that never
45/// end early (Battle_AdvanceCounterAndCheckChance).
46#define PSY_SEAL_BIND 7
47#define PSY_SEAL_LUFF 16
48
49/// The rate BattlePowerCalc and BattleRecoveryCalc scale by: 256 is 1.
50#define FULL_RATE 256
51
52/// A message operand that shows nothing: the event only waits for the player.
53#define MESSAGE_NONE -1
54
55/// Unit ids 0 to 7 are the party's; enemy units start at FIRST_ENEMY_UNIT.
56#define IS_PARTY_UNIT(unitId) ((unitId) >= 0 && (unitId) < 8)
57
58// The helpers below are macros because the bytes need them so: written as
59// inline functions they compile to different code.
60
61/// Holds a stat stage within STAT_STAGE_MIN to STAT_STAGE_MAX.
62#define CLAMP_STAT_STAGE(stage) \
63 { \
64 if ((stage) < STAT_STAGE_MIN) \
65 (stage) = STAT_STAGE_MIN; \
66 if ((stage) > STAT_STAGE_MAX) \
67 (stage) = STAT_STAGE_MAX; \
68 }
69
70/// Pushes one message for a unit of the party and another for an enemy.
71#define PUSH_SIDE_MESSAGE(unitId, partyMessage, enemyMessage) \
72 { \
73 if (IS_PARTY_UNIT(unitId)) \
74 PUSH_MESSAGE(partyMessage); \
75 else \
76 PUSH_MESSAGE(enemyMessage); \
77 }
78
79/// A guarding unit takes half the damage, or a tenth behind Total Defense.
80#define REDUCE_BY_GUARD(damage, unit) \
81 { \
82 s32 guard = (unit)->guardLevel; \
83 \
84 if (guard != 0) { \
85 if (guard == GUARD_DEFEND) \
86 (damage) /= 2; \
87 else \
88 (damage) /= 10; \
89 } \
90 }
91
92/// Applies the planned ability to one of its targets: first its damage or
93/// healing, then its added effect (a status, a stat change, a revival...).
94/// Nothing is shown here: each step queues battle events (the names and
95/// numbers a message needs, the message, the target's reactions) that
96/// BattleEvent_Playback then plays in order. The units themselves change at
97/// once: UnitCalcFar recalculates a unit's stats from its stages, and
98/// UnitRatioFar its HP and PP gauges after either changes.
99///
100/// Called for each target in turn by BattleEvent_Playback (an action with an
101/// effect) and BattlePres_RunActorEntries (one without). Changes the target's
102/// HP, PP, statuses, stat stages and turns, and its guard; the actor's HP or
103/// PP for a drain, and plan->hauntDamage; adds an enemy to
104/// gBattleWork->enemyIds when one is called; sets gBattleWork->challengeFlag
105/// bit 1 when the party is challenged; queues the events that show it all.
106/// It takes BattleRandomFar rolls, and borrows a heap block for the target's
107/// copy.
108///
109/// It runs in phases, each marked in the body:
110/// 1. Setup: a copy of the target as it was; the target's distance from the
111/// aimed place; the element's affinity (resisted, neutral or weak); the
112/// actor's power in the element.
113/// 2. The Djinn bonus roll, and whether the effect lands (hit).
114/// 3. Calling an ally (Ally Search, Search for Help, Speed Surge).
115/// 4. The effects that change the damage step: Ground, Torch, Severe Blow,
116/// instant death, a self-destruct, Psy Drain.
117/// 5. Damage and healing, by the ability's kind (attack, PP damage, HP and PP
118/// healing, Psynergy damage, an effect's damage).
119/// 6. The added effect, by the ability's effect: cures, heals, stat changes,
120/// revivals, statuses, drains, Break, the curse, guards.
121/// 7. Waking from damage, the recalculated stats, the redraw, and the evil
122/// spirit's share for a haunted actor.
123///
124/// plan : the action under way: the actor, the ability, its element and its
125/// targets, each with its distance from the aimed position, damage
126/// multiplier, critical hit and any hit roll made in advance.
127/// slot : which of the plan's targets (0 to plan->targetCount - 1).
128///
129#ifdef UBFIX
130void BattleTargetExec(struct BattlePlan *plan, s32 slot) {
131#else
132s32 BattleTargetExec(struct BattlePlan *plan, s32 slot) {
133#endif
134 s32 distance; // steps from the aimed position, 0 to 5
135 struct BattleAct *ability;
136 struct BattleUnit *actor;
137 s32 actorId;
138 s32 abilityId;
139 s32 elementBonus; // elemental power less resistance
140 struct BattleWork *work;
141 s32 halveDefense; // the attack counts half the target's Defense
142 s32 multiplier; // 0 on a miss, 2 or 3 for a double or triple blow
143 s32 damageDealt; // HP or PP the target lost (negative if healed)
144 s32 leaveOneHp; // the blow leaves the target 1 HP at most
145 s32 hit; // the ability's effect lands
146 s32 critical; // enum BattleCritical
147 s32 skipDamage; // no damage step: instant death replaces it
148 s32 kind; // how the power applies: enum AbilityKind
149 s32 affinity; // -1 a resisted element, 1 a weakness, else 0
150 // A copy of the target as it was, to measure each stat's change; a heap
151 // block freed at the end.
152 struct BattleUnit *savedTarget;
153 s32 elementPower; // the actor's power in the ability's element
154 s32 targetId;
155 s32 element;
156 struct BattleUnit *target;
157 s32 amount; // the damage, or the HP or PP restored
158 s32 unitSize;
159
160 //------------------------------------------------------------------
161 // 1. Setup
162 //------------------------------------------------------------------
163 elementBonus = 0;
164 work = gBattleWork;
165 halveDefense = 0;
166 damageDealt = 0;
167 leaveOneHp = 0;
168 skipDamage = 0;
169 affinity = 0;
170 unitSize = sizeof(struct BattleUnit);
171 savedTarget = MemAlloc(unitSize);
172
173 actorId = plan->actorId;
174 targetId = plan->targetIds[slot];
175 abilityId = plan->abilityId;
176 element = plan->element;
177 multiplier = plan->targetMultiplier[slot];
178 critical = plan->targetCritical[slot];
179
180 ability = BattleActGet(abilityId);
181 actor = UnitGetFar(actorId);
182 target = UnitGetFar(targetId);
183 Iwram_CopyWords(savedTarget, target, unitSize);
184
185 // The target's distance from the aimed position: every damage and
186 // healing table falls off with it. An ability that reaches the whole side
187 // treats every target as aimed at.
188 if (ability->range != ABILITY_RANGE_ALL) {
189 distance = plan->targetOffset[slot];
190 if (distance < 0)
191 distance = -distance;
192 } else {
193 distance = 0;
194 }
195
196 // The element's affinity: -1 when the target resists no element better
197 // than this one, 1 when it resists none worse. It picks the damage
198 // message ("damage.", "damage!" or "damage!!!").
199 if (element != ELEMENT_NONE) {
200 s32 resistance;
201 s32 i;
202
203 resistance = target->baseElement[element].resistance;
204 for (i = 0; i < NUM_ELEMENTS; i++) {
205 if (resistance < target->baseElement[i].resistance)
206 break;
207 }
208 if (i == NUM_ELEMENTS)
209 affinity = -1;
210
211 for (i = 0; i < NUM_ELEMENTS; i++) {
212 if (resistance > target->baseElement[i].resistance)
213 break;
214 }
215 if (i == NUM_ELEMENTS)
216 affinity = 1;
217 }
218
219 // The actor's power in the ability's element; items always use 100. (Here
220 // and below the element is read from the plan again, as the bytes need.)
221 if (plan->element >= ELEMENT_VENUS && plan->element <= ELEMENT_JUPITER &&
222 plan->command != CMD_ITEM)
223 elementPower = actor->element[plan->element].power;
224 else
225 elementPower = 100;
226
227 //------------------------------------------------------------------
228 // 2. The Djinn bonus, and whether the effect lands
229 //------------------------------------------------------------------
230 // A Djinni striking the target's weakness may earn the Djinn bonus: an
231 // enemy it fells then gives more coins and experience and a better chance
232 // of its item (BattleEnemy_RecordDefeat). The chance is the power less
233 // the resistance, plus 30, in percent; the event's operand is unused.
234 if (plan->command == CMD_DJINN &&
235 (plan->element >= ELEMENT_VENUS && plan->element <= ELEMENT_JUPITER) &&
236 affinity > 0) {
237 s32 chance;
238
239 chance = elementPower - target->element[plan->element].resistance;
240 chance += 30;
241 // Percent to a fraction of the 16-bit random range.
242 chance *= 65536 / 100;
243 if (chance > (BattleRandomFar() & 0xffff))
245 }
246
247 // Whether the effect lands: a roll made when the action was planned, or
248 // one made now, less likely the farther the target is.
249 kind = ability->flags & ABILITY_KIND_MASK;
250 {
251 s32 plannedHit;
252
253 plannedHit = plan->targetHit[slot];
254 if (plannedHit == HIT_NOT_ROLLED)
255 hit = BattleHitCheck(actorId, targetId, element, ability->effect,
256 gHitTable[distance]);
257 else
258 hit = plannedHit;
259 }
260
261 //------------------------------------------------------------------
262 // 3. Calling an ally
263 //------------------------------------------------------------------
264 // Calling for help: an enemy calls another of its kind, or a random member
265 // of its formation, into an empty enemy slot. Speed Surge is an enemy
266 // splitting itself in two. It needs the roll, room among the enemy
267 // sprite slots (EnemyCheck) and a free unit id.
268 if (ability->effect == ABILITY_EFFECT_CALL_ALLY ||
269 ability->effect == ABILITY_EFFECT_CALL_HELP) {
270 s32 classId;
271 s32 newUnitId;
272
273 classId = actor->classId;
274 newUnitId = EnemySlotGet();
275 if (ability->effect == ABILITY_EFFECT_CALL_HELP)
277 if (hit != 0 && EnemyCheck(classId) != 0 && newUnitId >= 0) {
278 s32 suffix;
279
280 // The newcomer's name letter; on the first duplicate the enemy
281 // already there takes a letter too.
283 if (suffix & 0x8000)
285 BattleUnitSetFar(newUnitId, classId, suffix & 0x7fff);
286 // Fill a removed enemy's place in the list, or append the newcomer.
287 {
288 s32 i;
289
290 for (i = 0; i < BATTLE_ENEMY_CAPACITY; i++) {
291 if (work->enemyIds[i] == BATTLE_UNIT_REMOVED) {
292 work->enemyIds[i] = newUnitId;
293 break;
294 }
295 if (work->enemyIds[i] == BATTLE_UNIT_LIST_END) {
296 work->enemyIds[i] = newUnitId;
297 work->enemyIds[i + 1] = BATTLE_UNIT_LIST_END;
298 break;
299 }
300 }
301 }
302 // Lay the enemies out again and put the newcomer's sprite in place
303 // (the anchors are 16.16 fixed point).
304 EnemyUpdate();
305 {
306 struct BattleObj *obj;
307
308 obj = BattleObjGet(newUnitId);
309 BattleObjMake(obj, newUnitId, obj->anchorX / 0x10000,
310 obj->anchorZ / 0x10000);
311 }
312 BattleObjPut();
313 {
314 s16 enemyIds[8];
315 s32 enemyCount;
316 s32 i;
317
318 enemyCount = BattleEnemyList(enemyIds);
319 for (i = 0; i < enemyCount; i++)
320 ObjMotRst((u16)enemyIds[i]);
321 }
323 if (abilityId != ABILITY_SPEED_SURGE)
324 PUSH_MESSAGE(MsgAppears);
325 else
326 PUSH_MESSAGE(MsgSplitOff);
327 } else if (abilityId == ABILITY_SPEED_SURGE) {
328 PUSH_MESSAGE(MsgSplitFail);
329 } else {
330 PUSH_MESSAGE(MsgNoOneCame);
331 }
332 }
333
334 //------------------------------------------------------------------
335 // 4. Effects that change how the damage step works
336 //------------------------------------------------------------------
337 if (hit != 0) {
338 if (ability->effect == ABILITY_EFFECT_GROUND) {
339 u32 i;
340
341 // Ground holds only a target with an action in this turn's queue.
342 hit = 0;
343 for (i = 0; i < BATTLE_ACTION_CAPACITY; i++) {
344 if (work->actions[i].unitId == targetId) {
345 hit = 1;
346 break;
347 }
348 }
349 } else if (ability->effect == ABILITY_EFFECT_HALVE_DEFENSE) {
350 halveDefense = 1;
351 } else if (ability->effect == ABILITY_EFFECT_LEAVE_ONE_HP) {
352 leaveOneHp = 1;
353 } else if (ability->effect == ABILITY_EFFECT_INSTANT_DEATH) {
354 skipDamage = 1;
355 } else if (ability->effect == ABILITY_EFFECT_SELF_DESTRUCT) {
356 // The actor leaves the battle (BattleActor_DestroyTemporaryObject).
357 if (actor->hp != 0)
359 } else if (ability->effect == ABILITY_EFFECT_DRAIN_PP) {
360 // Psy Drain does PP damage, and misses a target with no PP.
361 if (target->pp != 0)
363 else
364 hit = 0;
365 }
366 }
367
368 //------------------------------------------------------------------
369 // 5. Damage and healing
370 //------------------------------------------------------------------
371 // A downed target is skipped, unless the ability revives.
372 if (skipDamage == 0 &&
373 (target->hp != 0 || Battle_IsReviveEffectFar(ability->effect) != 0)) {
374 s32 pp;
375 s32 newHp;
376 s32 power;
377
378 switch (kind) {
379 // An attack: the actor's Attack against the target's Defense.
382 s32 hp;
383 s32 defense;
384 s32 baseDefense;
385 s32 pass;
386
387 hp = target->hp;
388 baseDefense = target->def;
389 defense = baseDefense;
390 if (halveDefense != 0)
391 defense = (u32)baseDefense / 2;
392 // This loop runs once, with pass 1, so its pass-0 code never runs:
393 // the element bonus always applies and the critical hit message is
394 // never shown.
395 for (pass = 1; pass <= 1; pass++) {
396 if (element != ELEMENT_NONE)
397 elementBonus = elementPower - target->element[element].resistance;
398 if (pass == 0)
399 elementBonus = 0;
400 power = ability->power;
402 amount =
403 BattleAtkCalc(actor->atk, defense, 0, elementBonus) * power / 10;
404 else
405 amount = BattleAtkCalc(actor->atk, defense, power, elementBonus);
406 amount *= multiplier;
407 // A critical hit adds a quarter (a half for an ability's), then
408 // the target's level / 5 + 6.
409 if (critical != CRITICAL_NONE) {
410 if (critical == CRITICAL_HIT)
411 amount = amount * 5 / 4;
412 else
413 amount = amount * 3 / 2;
414 amount += (target->level / 5) + 6;
415 if (pass == 0) {
417 {
418 s32 message;
419
420 // "A critical hit!", or "A bitter blow!" (the next message)
421 // when it lands on the party.
422 message = MsgCritical;
423 if (IS_PARTY_UNIT(targetId))
424 message += 1;
426 }
427 }
428 }
429 amount += BattleRandomFar() & 3;
430 REDUCE_BY_GUARD(amount, target);
431 if (amount <= 0)
432 amount = 1;
433 if (leaveOneHp != 0) {
434 if (amount < hp - 1) {
435 amount = hp - 1;
436 if (amount <= 0)
437 amount = 1;
438 }
439 }
440 // In the Djinn tutorial a Djinni's blow always leaves 1 HP.
442 plan->command == CMD_DJINN && hp <= amount) {
443 amount = hp - 1;
444 }
445 }
446 // The flinch, then "<target> takes <amount> damage"; a fall queues the
447 // defeat before its message, and a target still standing is redrawn.
448 hp -= amount;
452 // The affinity picks "damage.", "damage!" or "damage!!!".
453 PUSH_MESSAGE((IS_PARTY_UNIT(targetId) ? MsgDmgEmphP : MsgDmgEmphE) +
454 affinity);
455
456 if (hp <= 0) {
459 hp = 0;
460 PUSH_SIDE_MESSAGE(targetId, MsgGoesDown, MsgFelled);
461 } else
463 damageDealt = target->hp - hp;
464 target->hp = hp;
465 UnitRatioFar(targetId);
466 break;
467 }
468
470 if (ability->power == 0)
471 break;
472 pp = target->pp;
473 if (element != ELEMENT_NONE)
474 elementBonus = elementPower - target->element[element].resistance;
475 power = ability->power;
476 amount = BattlePowerCalc(power, elementBonus, FULL_RATE);
477 amount = amount * gPpLossTable[distance] / 100;
478 amount *= multiplier;
479 REDUCE_BY_GUARD(amount, target);
480 // Psy Drain takes no more PP than the target has.
481 if (ability->effect == ABILITY_EFFECT_DRAIN_PP && amount > pp)
482 amount = pp;
483
487 PUSH_MESSAGE(IS_PARTY_UNIT(targetId) ? MsgPpLossP : MsgPpLossE);
488 pp -= amount;
489 if (pp <= 0)
490 pp = 0;
492 damageDealt = target->pp - pp;
493 target->pp = pp;
494 UnitRatioFar(targetId);
495 break;
496 }
497
498 // Healing, no further than the maximum: "HP fully restored" there, else
499 // the amount restored.
501 if (ability->power == 0)
502 break;
503 newHp = target->hp;
504
505 // An ability with no element (an item) heals as if at power 100.
506 power = ability->power;
507 amount = BattleRecoveryCalc(
508 power, element == ELEMENT_NONE ? 100 : elementPower, FULL_RATE);
509 amount = amount * gHpRecoveryTable[distance] / 100;
510 amount *= multiplier;
511 amount += BattleRandomFar() & 3;
512 newHp += amount;
513 if (newHp > target->maxHp) {
514 newHp = target->maxHp;
515 amount = newHp - target->hp;
516 }
518 if (newHp == target->maxHp)
519 PUSH_MESSAGE(MsgHpFull);
520 else {
522 PUSH_MESSAGE(MsgHpRecover);
523 }
524 damageDealt = target->hp - newHp;
525 target->hp = newHp;
526 UnitRatioFar(targetId);
527 break;
528 }
529
530 // Never reached, as the kind is 0 to 15: PP damage that, unlike
531 // ABILITY_KIND_PP_DAMAGE, does not count as damage dealt.
532 case -1: {
533 if (ability->power == 0)
534 break;
535 pp = target->pp;
536 if (element != ELEMENT_NONE)
537 elementBonus = elementPower - target->element[element].resistance;
538 power = ability->power;
539 amount = BattlePowerCalc(power, elementBonus, FULL_RATE);
540 amount = amount * gPpDmgTable[distance] / 100;
541 amount *= multiplier;
542 REDUCE_BY_GUARD(amount, target);
546 PUSH_MESSAGE(IS_PARTY_UNIT(targetId) ? MsgDmgP : MsgDmgE);
547 pp -= amount;
548 if (pp <= 0)
549 pp = 0;
551 target->pp = pp;
552 UnitRatioFar(targetId);
553 break;
554 }
555
556 // HP damage from the ability's power, each kind with its own fall-off
557 // by distance.
560 case ABILITY_KIND_SUMMON: {
561 s32 pass;
562
563 if (ability->power == 0)
564 break;
565 newHp = target->hp;
566 // Runs once, like the attack's loop.
567 for (pass = 1; pass <= 1; pass++) {
568 if (element != ELEMENT_NONE)
569 elementBonus = elementPower - target->element[element].resistance;
570 if (pass == 0)
571 elementBonus = 0;
572 power = ability->power;
573 // A summon adds 3% of the target's max HP for each Djinni it costs.
574 if (plan->command == CMD_SUMMON) {
575 s32 maxHpPercent;
576
577 switch (abilityId) {
578 case ABILITY_VENUS:
579 case ABILITY_MERCURY:
580 case ABILITY_MARS:
581 case ABILITY_JUPITER:
582 maxHpPercent = 3;
583 break;
584 case ABILITY_RAMSES:
585 case ABILITY_NEREID:
586 case ABILITY_KIRIN:
587 case ABILITY_ATALANTA:
588 maxHpPercent = 6;
589 break;
590 case ABILITY_CYBELE:
591 case ABILITY_NEPTUNE:
592 case ABILITY_TIAMAT:
593 case ABILITY_PROCNE:
594 maxHpPercent = 9;
595 break;
596 case ABILITY_JUDGMENT:
597 case ABILITY_BOREAS:
598 case ABILITY_METEOR:
599 case ABILITY_THOR:
600 maxHpPercent = 12;
601 break;
602 // BUG: another id with summon-kind data leaves this uninitialized.
603 // It has no known summon bonus, so use its base power alone.
604#ifdef UBFIX
605 default:
606 maxHpPercent = 0;
607 break;
608#endif
609 }
610 power += target->maxHp * maxHpPercent / 100;
611 }
612 amount = BattlePowerCalc(power, elementBonus, FULL_RATE);
613 amount *= multiplier;
614 // The fall-off by distance. (The kind is masked again, as the bytes
615 // need.)
616 switch (kind & ABILITY_KIND_MASK) {
618 amount = amount * gHpDmgTable5[distance] / 100;
619 break;
621 amount = amount * gHpDmgTable8[distance] / 100;
622 break;
624 amount = amount * gHpDmgTable6[distance] / 100;
625 break;
626 }
627 amount += BattleRandomFar() & 3;
628 REDUCE_BY_GUARD(amount, target);
629 // In the Djinn tutorial a summon always fells its target.
631 plan->command == CMD_SUMMON && newHp > amount) {
632 amount = newHp;
633 }
634 }
638 PUSH_MESSAGE((IS_PARTY_UNIT(targetId) ? MsgDmgEmphP : MsgDmgEmphE) +
639 affinity);
640 newHp -= amount;
641
642 if (newHp <= 0) {
645 newHp = 0;
646 PUSH_MESSAGE(IS_PARTY_UNIT(targetId) ? MsgGoesDown : MsgFelled);
647 } else
649 damageDealt = target->hp - newHp;
650 target->hp = newHp;
651 UnitRatioFar(targetId);
652 break;
653 }
654
656 if (ability->power == 0)
657 break;
658 pp = target->pp;
659 power = ability->power;
660 amount = BattleRecoveryCalc(
661 power, element == ELEMENT_NONE ? 100 : elementPower, FULL_RATE);
662 amount = amount * gPpRecoveryTable[distance] / 100;
663 amount *= multiplier;
664 pp += amount;
665 if (pp > target->maxPp) {
666 pp = target->maxPp;
667 amount = pp - target->pp;
668 }
670 if (pp == target->maxPp)
671 PUSH_MESSAGE(MsgPpFull);
672 else {
674 PUSH_MESSAGE(MsgPpRecover);
675 }
676 target->pp = pp;
677 UnitRatioFar(targetId);
678 break;
679 }
680
681 // The kinds with no damage or healing (see enum AbilityKind).
682 case 0:
683 case 7:
684 case 9:
685 break;
686
687 // An effect ability: its power deals damage only if the effect lands.
689 if (hit != 0) {
690 if (ability->power == 0)
691 break;
692 newHp = target->hp;
693 if (element != ELEMENT_NONE)
694 elementBonus = elementPower - target->element[element].resistance;
695 power = ability->power;
696 amount = BattlePowerCalc(power, elementBonus, FULL_RATE);
697 amount *= multiplier;
698 amount = amount * gHpDmgTable[distance] / 100;
699 REDUCE_BY_GUARD(amount, target);
703 PUSH_MESSAGE(IS_PARTY_UNIT(targetId) ? MsgDmgP : MsgDmgE);
704 newHp -= amount;
705
706 if (newHp <= 0) {
709 newHp = 0;
710 PUSH_SIDE_MESSAGE(targetId, MsgGoesDown, MsgFelled);
711 } else
713 damageDealt = target->hp - newHp;
714 target->hp = newHp;
715 UnitRatioFar(targetId);
716 break;
717 }
720 PUSH_MESSAGE(MsgNoEffect);
721 break;
722 }
723 }
724
725 //------------------------------------------------------------------
726 // 6. The added effect: a status, a stat change, a cure or a revival
727 //------------------------------------------------------------------
728 // It needs the effect to have landed on a target still standing, unless
729 // it revives or drains. The target's name goes first, for the effect's
730 // first message; a message uses up the arguments pushed before it, so each
731 // later message pushes the name again.
733 if ((Battle_IsReviveEffectFar(ability->effect) != 0 || target->hp != 0 ||
734 BattleEffectDeadCheck(ability->effect) != 0) &&
735 hit != 0) {
736 switch (ability->effect) {
737 // Tonic cures every ailment; sleep goes without a message. Most of its
738 // messages push the name for the next after them; Restore below clears
739 // the arguments and pushes the name before each instead.
741 if (target->delusion != 0) {
742 target->delusion = 0;
743 PUSH_MESSAGE(MsgCureDelusion);
745 }
746 if (target->stun != 0) {
747 target->stun = 0;
750 PUSH_MESSAGE(MsgCureStun);
751 }
752 target->sleep = 0;
753 if (target->psySeal != 0) {
754 target->psySeal = 0;
755 PUSH_MESSAGE(MsgCureSeal);
757 }
758 if (target->grimReaper != 0) {
759 target->grimReaper = 0;
760 PUSH_MESSAGE(MsgCureCurse);
762 }
763 if (target->haunt != 0) {
764 target->haunt = 0;
765 PUSH_MESSAGE(MsgCureSpirit);
767 }
768 if (target->poison != 0) {
769 PUSH_MESSAGE(MsgCurePoison);
770 target->poison = 0;
771 }
773 break;
774
775 // Restore cures delusion, stun, sleep, the Psynergy seal and the Grim
776 // Reaper, but not poison or a haunting.
778 if (target->delusion != 0) {
779 target->delusion = 0;
782 PUSH_MESSAGE(MsgCureDelusion);
783 }
784 if (target->stun != 0) {
785 target->stun = 0;
788 PUSH_MESSAGE(MsgCureStun);
789 }
790 target->sleep = 0;
791 if (target->psySeal != 0) {
792 target->psySeal = 0;
795 PUSH_MESSAGE(MsgCureSeal);
796 }
797 if (target->grimReaper == 0)
798 break;
799 target->grimReaper = 0;
802 PUSH_MESSAGE(MsgCureCurse);
803 break;
804
807 s32 oldHp;
808 s32 maxHpBits;
809 s32 maxHp;
810 s32 newHp;
811 s32 restored;
812#ifndef UBFIX
813 u16 *stat;
814#endif
815
816 // UB: a pointer walks from hp down past it to maxHp, reading each stat
817 // twice, unsigned and signed, as the ROM does; the member reads behind
818 // UBFIX share one load each.
819#ifdef UBFIX
820 oldHp = (u16)target->hp;
821 newHp = target->hp;
822 maxHpBits = (u16)target->maxHp;
823 maxHp = target->maxHp;
824#else
825 stat = (u16 *)&target->hp;
826 oldHp = *stat--;
827 newHp = *(s16 *)(stat + 1);
828 stat--;
829 maxHpBits = *stat--;
830 maxHp = *(s16 *)(stat + 1);
831#endif
832 if (ability->effect == ABILITY_EFFECT_HEAL_HP_60)
833 newHp += maxHp * 60 / 100;
834 else
835 newHp += maxHp * 30 / 100;
836 if (newHp > (s16)maxHpBits)
837 newHp = (s16)maxHpBits;
838 restored = newHp - (s16)oldHp;
839 // Nothing to show if no HP came back, unless the ability heals.
840 if (restored == 0 && kind != ABILITY_KIND_HEAL_HP)
841 break;
842 if (newHp == (s16)maxHpBits)
843 PUSH_MESSAGE(MsgHpFull);
844 else {
846 PUSH_MESSAGE(MsgHpRecover);
847 }
848 target->hp = newHp;
849 UnitRatioFar(targetId);
850 break;
851 }
852
854 s32 oldPp;
855 s32 maxPp;
856 s32 newPp;
857 s32 restored;
858
859 newPp = target->pp;
860 maxPp = target->maxPp;
861 oldPp = newPp;
862 newPp += maxPp * 7 / 100;
863 if (newPp > maxPp)
864 newPp = maxPp;
865 restored = newPp - oldPp;
866 if (restored == 0 && kind != ABILITY_KIND_HEAL_PP)
867 break;
868 if (newPp == maxPp)
869 PUSH_MESSAGE(MsgPpFull);
870 else {
872 PUSH_MESSAGE(MsgPpRecover);
873 }
874 target->pp = newPp;
875 UnitRatioFar(targetId);
876 break;
877 }
878
879 // The stat changes. The amount shown is how much the stat itself moved,
880 // measured against the saved copy once UnitCalc has recalculated it, so
881 // a stage already at its limit shows 0; the turns start again either
882 // way. Zephyr and Vine set Agility's stage outright.
886 UnitCalcFar(targetId);
887 BattleEvPush(BATTLE_EVENT_NUMBER, target->agi - savedTarget->agi);
888 PUSH_MESSAGE(MsgAgiUp);
889 break;
890
894 UnitCalcFar(targetId);
895 BattleEvPush(BATTLE_EVENT_NUMBER, savedTarget->agi - target->agi);
896 PUSH_MESSAGE(MsgAgiDown);
897 break;
898
900 target->attackStage -= 1;
902 UnitCalcFar(targetId);
903 BattleEvPush(BATTLE_EVENT_NUMBER, savedTarget->atk - target->atk);
904 PUSH_MESSAGE(MsgAtkDown);
906 break;
907
909 target->attackStage -= 2;
911 UnitCalcFar(targetId);
912 BattleEvPush(BATTLE_EVENT_NUMBER, savedTarget->atk - target->atk);
913 PUSH_MESSAGE(MsgAtkDown);
915 break;
916
918 target->attackStage += 1;
920 UnitCalcFar(targetId);
921 BattleEvPush(BATTLE_EVENT_NUMBER, target->atk - savedTarget->atk);
922 PUSH_MESSAGE(MsgAtkUp);
924 break;
925
927 target->attackStage += 2;
929 UnitCalcFar(targetId);
930 BattleEvPush(BATTLE_EVENT_NUMBER, target->atk - savedTarget->atk);
931 PUSH_MESSAGE(MsgAtkUp);
933 break;
934
936 target->defenseStage -= 1;
938 UnitCalcFar(targetId);
939 BattleEvPush(BATTLE_EVENT_NUMBER, savedTarget->def - target->def);
940 PUSH_MESSAGE(MsgDefDown);
942 break;
943
945 target->defenseStage -= 2;
947 UnitCalcFar(targetId);
948 BattleEvPush(BATTLE_EVENT_NUMBER, savedTarget->def - target->def);
949 PUSH_MESSAGE(MsgDefDown);
951 break;
952
954 target->defenseStage += 1;
956 UnitCalcFar(targetId);
957 BattleEvPush(BATTLE_EVENT_NUMBER, target->def - savedTarget->def);
958 PUSH_MESSAGE(MsgDefUp);
960 break;
961
963 target->defenseStage += 2;
965 UnitCalcFar(targetId);
966 BattleEvPush(BATTLE_EVENT_NUMBER, target->def - savedTarget->def);
967 PUSH_MESSAGE(MsgDefUp);
969 break;
970
971 // The revivals bring back a downed target with all, half or 80% of its
972 // HP.
974 if (target->hp != 0)
975 break;
976 PUSH_MESSAGE(MsgRevived);
977 target->hp = target->maxHp;
978 UnitRatioFar(targetId);
979 break;
980
982 if (target->hp != 0)
983 break;
984 PUSH_MESSAGE(MsgRevived);
985 target->hp = target->maxHp / 2;
986 UnitRatioFar(targetId);
987 break;
988
990 if (target->hp != 0)
991 break;
992 PUSH_MESSAGE(MsgRevived);
993 target->hp = target->maxHp * 8 / 10;
994 UnitRatioFar(targetId);
995 break;
996
998 if (target->poison != 0)
999 PUSH_MESSAGE(MsgCurePoison);
1000 target->poison = 0;
1001 break;
1002
1003 // A Resistance change needs no recalculation to show: it is the change
1004 // in stages times RESISTANCE_PER_STAGE.
1006 target->resistanceStage -= 1;
1009 (savedTarget->resistanceStage - target->resistanceStage) *
1011 PUSH_MESSAGE(MsgResDown);
1013 break;
1014
1016 target->resistanceStage -= 2;
1019 (savedTarget->resistanceStage - target->resistanceStage) *
1021 PUSH_MESSAGE(MsgResDown);
1023 break;
1024
1026 target->resistanceStage += 1;
1029 (target->resistanceStage - savedTarget->resistanceStage) *
1031 PUSH_MESSAGE(MsgResUp);
1033 break;
1034
1036 target->resistanceStage += 2;
1039 (target->resistanceStage - savedTarget->resistanceStage) *
1041 PUSH_MESSAGE(MsgResUp);
1043 break;
1044
1045 // Poison does nothing to a poisoned target, deadly poison nothing to one
1046 // that already has it.
1048 if (target->poison != 0)
1049 break;
1050 PUSH_MESSAGE(MsgPoisoned);
1051 target->poison = POISON_NORMAL;
1052 break;
1053
1055 if (target->poison > POISON_NORMAL)
1056 break;
1057 PUSH_MESSAGE(MsgVenom);
1058 target->poison = POISON_DEADLY;
1059 break;
1060
1062 PUSH_MESSAGE(MsgDelusion);
1063 target->delusion = STATUS_TURNS;
1064 break;
1065
1067 PUSH_MESSAGE(MsgConfused);
1068 target->confusion = STATUS_TURNS;
1069 break;
1070
1071 case ABILITY_EFFECT_CHARM: // "X's heart has been stolen!"
1072 PUSH_MESSAGE(MsgCharmed);
1073 target->charm = STATUS_TURNS;
1074 break;
1075
1077 PUSH_MESSAGE(MsgStunned);
1078 target->stun = STATUS_TURNS;
1079 break;
1080
1082 PUSH_MESSAGE(MsgAsleep);
1083 target->sleep = STATUS_TURNS;
1084 break;
1085
1087 PUSH_SIDE_MESSAGE(targetId, MsgPsyBlock, MsgPsySeal);
1088 target->psySeal |= PSY_SEAL_BIND;
1089 break;
1090
1092 PUSH_SIDE_MESSAGE(targetId, MsgPsyBlock, MsgPsySeal);
1093 target->psySeal |= PSY_SEAL_LUFF;
1094 break;
1095
1096 // Instant death: "X goes down!" on the party, Drown's "X suffocates!"
1097 // or "X's spirit has been drained!" on an enemy.
1100 if (target->side == BATTLE_UNIT_PARTY)
1101 PUSH_MESSAGE(MsgKoDown);
1102 else if (abilityId == ABILITY_DROWN)
1103 PUSH_MESSAGE(MsgSuffocate);
1104 else
1105 PUSH_MESSAGE(MsgSpiritDrain);
1106 target->hp = 0;
1107 UnitRatioFar(targetId);
1108 break;
1109
1111 PUSH_MESSAGE(MsgRefrain);
1112 target->refrain = STATUS_TURNS;
1113 break;
1114
1116 PUSH_MESSAGE(MsgReflect);
1117 target->reflect = STATUS_TURNS;
1118 break;
1119
1120 // The drains give the damage dealt back to the actor: Drain all of it
1121 // as HP, Sap half.
1124 s32 newHp;
1125
1126 newHp = actor->hp;
1127 amount = damageDealt;
1128 if (ability->effect == ABILITY_EFFECT_DRAIN_HALF_HP)
1129 amount /= 2;
1130 newHp += amount;
1131 if (newHp > actor->maxHp) {
1132 newHp = actor->maxHp;
1133 amount = newHp - actor->hp;
1134 }
1137 if (newHp == actor->maxHp)
1138 PUSH_MESSAGE(MsgHpFull);
1139 else {
1141 PUSH_MESSAGE(MsgHpRecover);
1142 }
1143 actor->hp = newHp;
1144 UnitRatioFar(actorId);
1145 break;
1146 }
1147
1148 // Psy Drain gives the PP taken.
1150 s32 newPp;
1151 s32 drained;
1152
1153 newPp = actor->pp;
1154 drained = damageDealt;
1155 newPp += drained;
1156 if (newPp > actor->maxPp) {
1157 newPp = actor->maxPp;
1158 drained = newPp - actor->pp;
1159 }
1162 if (newPp == actor->maxPp)
1163 PUSH_MESSAGE(MsgPpFull);
1164 else {
1166 PUSH_MESSAGE(MsgPpRecover);
1167 }
1168 actor->pp = newPp;
1169 UnitRatioFar(actorId);
1170 break;
1171 }
1172
1173 // Psy Leech: a tenth of the damage as PP, as far as the target has it
1174 // and the actor has room.
1176 amount = damageDealt / 10;
1177 if (target->pp < amount)
1178 amount = target->pp;
1179 if (actor->pp + amount > actor->maxPp)
1180 amount = actor->maxPp - actor->pp;
1181 if (amount == 0)
1182 break;
1184 PUSH_SIDE_MESSAGE(targetId, MsgLeechTake, MsgLeechGain);
1185 Unit_AddPpFar(actorId, amount);
1186 break;
1187
1188 // Break undoes every raised stat and the element stages.
1190 if (target->attackStage > 0) {
1191 target->attackStage = 0;
1192 target->attackTurns = 0;
1193 }
1194 if (target->defenseStage > 0) {
1195 target->defenseStage = 0;
1196 target->defenseTurns = 0;
1197 }
1198 if (target->resistanceStage > 0) {
1199 target->resistanceStage = 0;
1200 target->resistanceTurns = 0;
1201 }
1202 if (target->agilityStage > 0)
1203 target->agilityStage = 0;
1204 target->elementLevels[ELEMENT_VENUS] = 0;
1205 target->elementLevels[ELEMENT_MERCURY] = 0;
1206 target->elementLevels[ELEMENT_MARS] = 0;
1207 target->elementLevels[ELEMENT_JUPITER] = 0;
1208 PUSH_MESSAGE(MsgBuffsReset);
1209 break;
1210
1212 PUSH_MESSAGE(MsgEvilSpirit);
1213 target->haunt = 1;
1214 break;
1215
1216 // The Grim Reaper counts down to the target's death; each later curse
1217 // brings it a turn closer.
1219 if (target->grimReaper == 0) {
1220 PUSH_MESSAGE(MsgDeathCurse);
1221 target->grimReaper = CURSE_TURNS;
1222 break;
1223 }
1224 if (target->grimReaper <= 1)
1225 break;
1226 target->grimReaper -= 1;
1228 PUSH_MESSAGE(MsgDeathCount);
1229 break;
1230
1231 // Kite: the target acts twice in the next turn (the count drops at each
1232 // turn's end).
1234 PUSH_MESSAGE(MsgReadies);
1235 target->ready = 2;
1236 break;
1237
1238 // The target rises to the challenge and won't flee; on the party, no one
1239 // may run ("No time to run!").
1241 PUSH_MESSAGE(MsgChallenge);
1242 target->challenge = 1;
1243 if (IS_PARTY_UNIT(targetId))
1244 work->challengeFlag |= 2;
1245 break;
1246
1247 // Ground: the target loses its next action.
1249 PUSH_MESSAGE(MsgImmobile);
1250 target->immobile = 1;
1251 break;
1252
1253 // The guards: the message shows either way, but a guard is never lowered.
1255 PUSH_MESSAGE(MsgAura);
1256 if (target->guardLevel > 0)
1257 break;
1258 target->guardLevel = GUARD_DEFEND;
1259 break;
1260
1262 PUSH_MESSAGE(MsgAura2);
1263 if (target->guardLevel > GUARD_DEFEND)
1264 break;
1266 break;
1267
1268 // Contain, Threaten and Tremble do nothing; the player only reads their
1269 // message.
1272 break;
1273
1274 default:
1275 break;
1276 }
1277 }
1278
1279 //------------------------------------------------------------------
1280 // 7. Waking from damage, and the recalculated stats
1281 //------------------------------------------------------------------
1283
1284 // Damage wakes a sleeping target one time in four, unless it fell asleep
1285 // this turn (its count still at STATUS_TURNS).
1286 if (target->hp != 0 && target->sleep != 0 && target->sleep < STATUS_TURNS &&
1287 damageDealt > 0 && (BattleRandomFar() & 3) == 0) {
1288 target->sleep = 0;
1290 PUSH_MESSAGE(MsgWakes);
1291 }
1292 MemFree(savedTarget);
1293 UnitCalcFar(targetId);
1294 // The party's status is drawn now, not through the queue.
1296 if (target->hp != 0)
1298 // A haunted actor: one time in four the evil spirit takes a quarter of the
1299 // damage it dealt (at least 1), at the end of its turn
1300 // (BattleUnit_ProcessTurnEnd).
1301 if (actor->haunt != 0 && (BattleRandomFar() & 3) == 0 && damageDealt > 0) {
1302 s32 share;
1303
1304 share = damageDealt >> 2;
1305 if (share == 0)
1306 share = 1;
1307 plan->hauntDamage += share;
1308 }
1309}
1310
1311/// The ability Psy Drain, Psynergy that costs no PP.
1312#define ABILITY_PSY_DRAIN 126
1313
1314/// Returns 1 for an ability that costs PP, and for Psy Drain, which costs
1315/// none.
1316/// Called by BattleCommand_BuildPlan (a cast's staging) and
1317/// BattleCommand_SelectAutomatic (an enemy's PP and seal checks).
1319 u8 pp;
1320
1321 if (abilityId == ABILITY_PSY_DRAIN)
1322 return 1;
1323 pp = BattleActGet(abilityId)->ppCost;
1324 // The sign bit of -pp | pp: 1 when pp is not 0, with no branch.
1325 return (u32)((0 - pp) | pp) >> 31;
1326}
@ ABILITY_VENUS
The summons: four to an element, in the order of their Djinn costs, each element's six ids apart.
Definition ability_ids.h:29
@ ABILITY_THOR
Definition ability_ids.h:44
@ ABILITY_BOREAS
Definition ability_ids.h:36
@ ABILITY_JUDGMENT
Definition ability_ids.h:32
@ ABILITY_METEOR
Definition ability_ids.h:40
@ ABILITY_KIRIN
Definition ability_ids.h:38
@ ABILITY_SPEED_SURGE
An enemy that splits itself in two. Its message: "attempts to divide".
Definition ability_ids.h:46
@ ABILITY_NEPTUNE
Definition ability_ids.h:35
@ ABILITY_MERCURY
Definition ability_ids.h:33
@ ABILITY_DROWN
Definition ability_ids.h:26
@ ABILITY_RAMSES
Definition ability_ids.h:30
@ ABILITY_JUPITER
Definition ability_ids.h:41
@ ABILITY_TIAMAT
Definition ability_ids.h:39
@ ABILITY_NEREID
Definition ability_ids.h:34
@ ABILITY_PROCNE
Definition ability_ids.h:43
@ ABILITY_MARS
Definition ability_ids.h:37
@ ABILITY_CYBELE
Definition ability_ids.h:31
@ ABILITY_ATALANTA
Definition ability_ids.h:42
s32 BattleEnemyList(s16 *unitIds)
Lists the enemy units present, ended by BATTLE_UNIT_LIST_END, and returns how many there are (0 when ...
Definition encounter.c:401
u32 BattleEvPush(u32 opcode, u32 operand)
Queues one battle event (enum BattleEventOpcode) with its operand, and returns the opcode.
Definition events.c:321
struct BattleObj * BattleObjGet(s32 unitId)
Returns unitId's battle object, through its place, or null for a unit with no place.
Definition objects.c:480
void BattleObjPut(void)
Readies the sprite of every unit in the battle (releasing those of units that left) and sets each one...
Definition sprites.c:13
s32 EnemyCheck(s32 classId)
Returns 1 if an enemy of class classId would find room among the enemy sprite slots: one free slot fo...
Definition sprites.c:41
s32 gPpLossTable[]
s32 gHpDmgTable6[]
s32 gPpRecoveryTable[]
s32 gHpDmgTable8[]
s32 gHpDmgTable5[]
u8 gHitTable[]
s32 gHpRecoveryTable[]
s32 gHpDmgTable[]
s32 gPpDmgTable[]
@ BATTLE_EVENT_DEFEAT
the unit falls; an enemy fades out
@ BATTLE_EVENT_UNIT_NAME
a unit's name for the next message
@ BATTLE_EVENT_MARK
sets gBattleDisplayWork->marked
@ BATTLE_EVENT_DJINN_BONUS
later defeats give the Djinn bonus
@ BATTLE_EVENT_HIT
the unit flinches and flashes
@ BATTLE_EVENT_CLEAR_ARGUMENTS
forgets the names and numbers pushed
@ BATTLE_EVENT_REFRESH_UNIT
the unit's status icons, colour, pose
@ BATTLE_EVENT_REMOVE_UNIT
an enemy leaves the battle
@ BATTLE_EVENT_MESSAGE_NO_WAIT
a message, going straight on
@ BATTLE_EVENT_NUMBER
a number for the next message
What every part of the battle object shares: its includes, in their original order,...
#define POISON_DEADLY
#define GUARD_TOTAL_DEFENSE
#define POISON_NORMAL
BattleUnit.poison: the poison takes a tenth of the maximum HP at the end of each turn,...
#define GUARD_DEFEND
BattleUnit.guardLevel: Defend and Granite halve the damage, Total Defense and Flash cut it to a tenth...
@ BATTLE_UNIT_LIST_END
ends the list
@ BATTLE_UNIT_REMOVED
a place whose unit has left the battle
@ BATTLE_UNIT_PARTY
@ CMD_DJINN
@ CMD_ITEM
@ CMD_SUMMON
#define HIT_NOT_ROLLED
BattlePlan.targetHit while the hit is still to be rolled, as the ability lands; BattleCommand_BuildPl...
Definition battle_type.h:22
@ CRITICAL_NONE
Definition battle_type.h:15
@ CRITICAL_HIT
an attack's: a quarter more damage
Definition battle_type.h:16
@ ABILITY_KIND_PP_DAMAGE
Crazy Voice: the target loses PP.
Definition battle_unit.h:69
@ ABILITY_KIND_DAMAGE
HP damage from the power (Psynergy).
Definition battle_unit.h:66
@ ABILITY_KIND_HEAL_PP
Ember, Psy Crystal, Adept Ring.
Definition battle_unit.h:70
@ ABILITY_KIND_HEAL_HP
Cure, Fizz, healing items.
Definition battle_unit.h:62
@ ABILITY_KIND_EFFECT
damage only if the effect lands
Definition battle_unit.h:63
@ ABILITY_KIND_ATTACK
Attack vs Defense, plus the power.
Definition battle_unit.h:64
@ ABILITY_KIND_UNLEASHED_DAMAGE
an enemy's. Shown as X unleashes Y!
Definition battle_unit.h:67
@ ABILITY_KIND_SUMMON
the summons: damage scaled by distance
Definition battle_unit.h:68
@ ABILITY_KIND_ATTACK_MULTIPLIED
that attack times power / 10
Definition battle_unit.h:65
@ NUM_ELEMENTS
Definition battle_unit.h:41
@ ELEMENT_MARS
Definition battle_unit.h:38
@ ELEMENT_MERCURY
Definition battle_unit.h:37
@ ELEMENT_VENUS
Definition battle_unit.h:36
@ ELEMENT_JUPITER
Definition battle_unit.h:39
@ ELEMENT_NONE
Definition battle_unit.h:40
@ ABILITY_EFFECT_CHALLENGE
no ability has it
@ ABILITY_EFFECT_LEAVE_ONE_HP
Severe Blow, Thrash.
@ ABILITY_EFFECT_SEAL_2_TURNS
Luff.
@ ABILITY_EFFECT_HAUNT
Haunt.
@ ABILITY_EFFECT_INSTANT_DEATH
Condemn.
@ ABILITY_EFFECT_DRAIN_HALF_HP
Sap.
@ ABILITY_EFFECT_ATTACK_DOWN
Blunt.
Definition battle_unit.h:85
@ ABILITY_EFFECT_POISON
Taint.
Definition battle_unit.h:94
@ ABILITY_EFFECT_BREAK
Break: raised stats undone.
@ ABILITY_EFFECT_HALVE_DEFENSE
Torch.
@ ABILITY_EFFECT_SELF_DESTRUCT
Dynamite, Mortal Blow.
@ ABILITY_EFFECT_HEAL_HP_30
Spritz: 30%.
@ ABILITY_EFFECT_SEAL
Bind: Psynergy sealed.
@ ABILITY_EFFECT_CURE_ALL
Tonic.
@ ABILITY_EFFECT_RESISTANCE_UP
Resist.
Definition battle_unit.h:91
@ ABILITY_EFFECT_ATTACK_UP_2
Impact: two stages.
Definition battle_unit.h:82
@ ABILITY_EFFECT_ATTACK_UP
High Impact: one stage.
Definition battle_unit.h:83
@ ABILITY_EFFECT_THREATEN
Contain, Threaten, Tremble.
@ ABILITY_EFFECT_TOTAL_DEFENSE
Total Defense, Flash.
@ ABILITY_EFFECT_REVIVE_HALF
Quartz.
@ ABILITY_EFFECT_DRAIN_HP
Drain.
@ ABILITY_EFFECT_STUN
Paralyze.
Definition battle_unit.h:99
@ ABILITY_EFFECT_SLEEP
Sleep.
@ ABILITY_EFFECT_CHARM
Charm.
Definition battle_unit.h:98
@ ABILITY_EFFECT_RESISTANCE_DOWN_2
Weaken.
Definition battle_unit.h:92
@ ABILITY_EFFECT_DEFENSE_UP
Protect.
Definition battle_unit.h:87
@ ABILITY_EFFECT_REGENERATE
Regenerate.
@ ABILITY_EFFECT_RESTORE
Restore, Elixir.
Definition battle_unit.h:80
@ ABILITY_EFFECT_REVIVE_80
Dew: 80% of max HP.
@ ABILITY_EFFECT_ATTACK_DOWN_2
Dull.
Definition battle_unit.h:84
@ ABILITY_EFFECT_DEFENSE_DOWN
Debilitate.
Definition battle_unit.h:89
@ ABILITY_EFFECT_HEAL_HP_60
Fizz: 60% of max HP.
@ ABILITY_EFFECT_RESISTANCE_UP_2
Ward.
Definition battle_unit.h:90
@ ABILITY_EFFECT_CALL_ALLY
Ally Search, Speed Surge.
@ ABILITY_EFFECT_CURSE
Curse: the Grim Reaper's countdown.
@ ABILITY_EFFECT_RESISTANCE_DOWN
Enfeeble.
Definition battle_unit.h:93
@ ABILITY_EFFECT_DEFENSE_UP_2
Guard.
Definition battle_unit.h:86
@ ABILITY_EFFECT_AGILITY_DOWN
Vine.
@ ABILITY_EFFECT_DEFEND
Defend, Granite.
@ ABILITY_EFFECT_REVIVE
Revive, Water of Life: full HP.
Definition battle_unit.h:81
@ ABILITY_EFFECT_GROUND
Ground: the target's turn lost.
@ ABILITY_EFFECT_DELUSION
Delude.
Definition battle_unit.h:96
@ ABILITY_EFFECT_CALL_HELP
Search for Help.
@ ABILITY_EFFECT_DRAIN_PP
Psy Drain.
@ ABILITY_EFFECT_CONFUSION
Confuse.
Definition battle_unit.h:97
@ ABILITY_EFFECT_HEAL_PP_7
Ember: 7% of max PP.
@ ABILITY_EFFECT_REFLECT
Reflect.
@ ABILITY_EFFECT_DEADLY_POISON
Poison.
Definition battle_unit.h:95
@ ABILITY_EFFECT_PSY_LEECH
Psy Leech.
@ ABILITY_EFFECT_CURE_POISON
Cure Poison, Antidote.
Definition battle_unit.h:79
@ ABILITY_EFFECT_AGILITY_UP
Zephyr.
@ ABILITY_EFFECT_DEFENSE_DOWN_2
Impair.
Definition battle_unit.h:88
@ ABILITY_EFFECT_ACT_TWICE
Kite.
#define ABILITY_KIND_MASK
The low four bits of BattleAct.flags: how BattleTargetExec applies the ability's power.
Definition battle_unit.h:52
#define ABILITY_RANGE_ALL
BattleAct.range of an ability that reaches every unit on the side, each as if aimed at.
Definition battle_unit.h:46
struct BattleWork * gBattleWork
@ BATTLE_ENEMY_CAPACITY
Definition battle_work.h:66
@ BATTLE_ACTION_CAPACITY
Definition battle_work.h:66
struct BattleUnit * UnitGetFar(u32 owner)
s32 BattleUnitSetFar(s32 unitId, s32 enemyId, s32 suffix)
s16 Unit_AddPpFar(s32 owner, s32 delta)
s32 BattleHitCheck(s32 caster, s32 target, s32 resistance_category, s32 effect_id, s32 success_scale)
Far call to BattleFx_RollSuccess.
s32 GameFlag_TestFar(s32 flag)
s32 BattlePowerCalc(s32 power, s32 bonus, s32 rate)
Far call to Battle_CalcPsynergyPower.
void UnitRatioFar(s32 owner_id)
struct BattleAct * BattleActGet(s32 actionId)
Far call to BattleAction_GetDirect.
s32 BattleRecoveryCalc(s32 power, s32 element, s32 rate)
Far call to Battle_CalcRecovery.
s32 Battle_IsReviveEffectFar(s32 effect_id)
s32 BattleRandomFar(void)
void UnitCalcFar(s32 owner)
s32 BattleAtkCalc(s32 atk, s32 def, s32 add, s32 bonus)
Far call to Battle_CalcAttackDamage.
void PartyStatusDrawFar(s32 flags)
#define FLAG_DJINN_TUTORIAL
Definition flags.h:69
s32 BattleEnemy_LetterFirstOfClass(s32 classId)
Gives the first enemy of the class on the field the first letter (0), appended to its name,...
Definition formation.c:362
s32 BattleGroupRandom(s32 recordId)
Returns the class id of a random member of the formation, among those that may appear (maximum count ...
Definition formation.c:407
s32 BattleEnemy_ClaimNameLetter(s32 classId, s32 unused)
Gives an enemy of the class its name letter, which tells it from others of its kind,...
Definition formation.c:269
void * MemAlloc(s32 size)
Allocates size bytes (rounded up to words) with no slot, from IWRAM when they fit and EWRAM otherwise...
Definition heap.c:115
static s32 Iwram_CopyWords(volatile void *destination, const volatile void *source, s32 size)
Definition iwram_call.h:85
void MemFree(void *buffer)
void ObjMotRst(s32 unitId)
Sends unitId back to its spot, landing there, and turns it to face the centre.
Definition motion.c:17
s32 EnemySlotGet(void)
Returns the id of the first free enemy unit, or -1 when all six are taken.
Definition placement.c:328
void BattleObjMake(struct BattleObj *actor, s32 unitId, s32 x, s32 y)
Creates the battle object of unit at (x, y) on the battlefield, in pixels: its sprite (an enemy's fro...
Definition placement.c:24
s32 EnemyUpdate(void)
Lays the enemies out again and moves each one's object anchor to its new place.
Definition placement.c:348
An ability's definition, a row of gBattleAction_DefinitionTable: its targeting, kind,...
u8 ppCost
PP the ability costs.
u8 effect
enum AbilityEffect
u8 range
the aimed unit and range - 1 either side, or ABILITY_RANGE_ALL
u8 flags
bits: 7 usable in battle | 6 usable in the field | 5..4 clear in every row | 3..0 kind (ABILITY_KIND_...
u16 power
what the kind applies, 0 for none
s16 resistance
Elemental resistance to it.
Definition battle_unit.h:29
s16 power
Elemental power in the element.
Definition battle_unit.h:28
A unit's place on the battlefield: its sprite object, resources and animations, its anchor spot and s...
s32 anchorZ
16.16 fixed-point pixels
s32 anchorX
16.16 fixed-point pixels
One action, as BattleCommand_BuildPlan lays it out for BattleTargetExec to apply target by target.
Definition battle_type.h:26
u32 hauntDamage
The damage the evil spirit deals a haunted actor at the end of its turn.
Definition battle_type.h:47
u8 actorId
the acting unit
Definition battle_type.h:27
u8 targetIds[BATTLE_TARGET_CAPACITY]
in battlefield order
Definition battle_type.h:29
s8 targetHit[BATTLE_TARGET_CAPACITY]
1, 0 or HIT_NOT_ROLLED
Definition battle_type.h:35
s8 targetOffset[BATTLE_TARGET_CAPACITY]
Each target's place relative to the aimed one: negative to one side.
Definition battle_type.h:31
s8 targetCritical[BATTLE_TARGET_CAPACITY]
enum BattleCritical
Definition battle_type.h:34
s16 command
enum BattleCommand
Definition battle_type.h:36
s32 element
enum Element
Definition battle_type.h:39
s8 targetMultiplier[BATTLE_TARGET_CAPACITY]
The damage multiplier: 0 a miss, 1 a blow, 2 or 3 a double or triple.
Definition battle_type.h:33
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 agilityTurns
the Agility change's turns left, 5 when cast
u8 immobile
set by Ground: the next action is lost
s8 elementLevels[4]
A summon's boost to each element: the Djinn of that element the summon spent, adding (n * n + n) * 5 ...
u8 grimReaper
The Grim Reaper's countdown: 7 when cursed, down one at each turn's end; at 0 the unit falls.
u8 psySeal
bits: 7..3 Luff (16 when cast, minus 8 a turn) | 2..0 Bind's turns (7 when cast).
u8 side
BATTLE_UNIT_ABSENT (0), BATTLE_UNIT_ENEMY (1) or BATTLE_UNIT_PARTY (2).
s16 maxPp
0 to 1999 for a character, 9999 for an enemy (UnitCalc)
s8 poison
1 poisoned, 2 deadly poison
u8 haunt
set while an evil spirit grips the unit
s16 hp
current HP, 0 when downed
struct BattleElementStats element[4]
Power and resistance in each element, 0 to 200, after equipment, element levels and Resistance stages...
s16 pp
current PP
s8 challenge
Set by the challenge effect: an enemy with it never chooses to flee.
u8 classId
Who the unit is: a character's id (ISAAC to JENNA, below 8) or an enemy's id (BattleUnitSet).
struct BattleElementStats baseElement[4]
Power and resistance before equipment and stages: an enemy's from its element preset,...
s16 maxHp
0 to 1999 for a character, 9999 for an enemy (UnitCalc)
s8 agilityStage
The Agility stage: -4 (Vine) to 8 (Zephyr, twice the Agility).
u8 refrain
Regenerate's turns left, 7 when cast.
s8 reflect
Reflect's turns left, 7 when cast.
u16 atk
Attack, 0 to 999 (UnitCalc).
s8 guardLevel
0, GUARD_DEFEND (1, half the damage) or GUARD_TOTAL_DEFENSE (2, a tenth); set by Defend and Granite,...
u8 level
1 to 99 for a character
u8 attackTurns
Each stat change: the turns it has left, and its stage (UnitCalc: an eighth of Attack,...
u16 agi
Agility, 0 to 999: the order of the round's actions.
u16 def
Defense, 0 to 999 (UnitCalc).
u8 delusion
Delusion to sleep: the turns each status has left, 7 when it lands; each may end sooner at the end of...
u8 ready
Kite's extra action: 2 when cast, down one at each turn's end; while nonzero UnitCalc gives the unit ...
The battle's own work: its enemies, units and their objects, the turn's actions, the spoils,...
Definition battle_work.h:71
u8 partyStatus
PARTY_STATUS_SUMMONS or PARTY_STATUS_NO_SUMMONS.
Definition battle_work.h:86
s32 enemyGroup
the encounter's enemy formation
Definition battle_work.h:72
u8 challengeFlag
bits: 1 a party unit was challenged; nonzero, the party cannot run
Definition battle_work.h:89
struct BattleActionRecord actions[BATTLE_ACTION_CAPACITY]
The actions chosen for the turn.
s16 enemyIds[BATTLE_ENEMY_CAPACITY+1]
ends with BATTLE_UNIT_LIST_END
#define FULL_RATE
The rate BattlePowerCalc and BattleRecoveryCalc scale by: 256 is 1.
Definition target_exec.c:50
#define PSY_SEAL_BIND
BattleUnit.psySeal: Bind's seal counts its seven turns in the low three bits, and may end sooner; Luf...
Definition target_exec.c:46
#define CURSE_TURNS
The Grim Reaper's countdown.
Definition target_exec.c:41
#define IS_PARTY_UNIT(unitId)
Unit ids 0 to 7 are the party's; enemy units start at FIRST_ENEMY_UNIT.
Definition target_exec.c:56
#define AGILITY_CHANGE_TURNS
Definition target_exec.c:38
#define REDUCE_BY_GUARD(damage, unit)
A guarding unit takes half the damage, or a tenth behind Total Defense.
Definition target_exec.c:80
#define AGILITY_STAGE_DOUBLED
Zephyr's Agility stage, beyond the usual highest: twice the Agility.
Definition target_exec.c:31
#define PUSH_SIDE_MESSAGE(unitId, partyMessage, enemyMessage)
Pushes one message for a unit of the party and another for an enemy.
Definition target_exec.c:71
#define MESSAGE_NONE
A message operand that shows nothing: the event only waits for the player.
Definition target_exec.c:53
#define STAT_CHANGE_TURNS
Definition target_exec.c:37
s32 BattleEffectDeadCheck(s32 effect)
Returns 1 for an effect that still applies when the ability's damage has just knocked its target out:...
Definition target_exec.c:13
s32 BattleTargetExec(struct BattlePlan *plan, s32 slot)
Applies the planned ability to one of its targets: first its damage or healing, then its added effect...
#define CLAMP_STAT_STAGE(stage)
Holds a stat stage within STAT_STAGE_MIN to STAT_STAGE_MAX.
Definition target_exec.c:62
#define RESISTANCE_PER_STAGE
A Resistance stage adds 20 to the resistance to every element (UnitCalc).
Definition target_exec.c:33
#define STATUS_TURNS
Definition target_exec.c:39
#define STAT_STAGE_MIN
A stat stage changes the stat by an eighth of it (UnitCalc), from half at the lowest to one and a hal...
Definition target_exec.c:28
u32 Ability_IsPsynergy(s32 abilityId)
Returns 1 for an ability that costs PP, and for Psy Drain, which costs none.
#define PSY_SEAL_LUFF
Definition target_exec.c:47
#define ABILITY_PSY_DRAIN
The ability Psy Drain, Psynergy that costs no PP.
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
unsigned char u8
Definition types.h:11
#define PUSH_MESSAGE(message)
Pushes a message to show, waiting for the player to read it.
Definition battle.h:64