Alchemy
Loading...
Searching...
No Matches

battle_calc.c

Go to the documentation of this file.
1/// @file
2/// Battle arithmetic: element multipliers, random rolls and success rates.
3/// Part of game.c, the game object, compiled only through it.
4
5#include "../game_internal.h"
6
7/// Returns the multiplier (256 = 1x) for an element difference `input`, read
8/// from a five-point line (gElement_PowerResistCurve); with `halve` the effect
9/// of the difference is half. Called by the damage and recovery formulas
10/// below.
12 s32 minimumDifference;
13 s32 maximumDifference;
14 s32 pointCount;
15 s32 pointIndex;
16 s32 multiplier;
17 s32 differenceSpan;
18 s32 multiplierSpan;
19
20 maximumDifference = gElement_PowerResistCurve[0][0];
21 minimumDifference = gElement_PowerResistCurve[4][0];
22 pointCount = 5;
23 if (input > maximumDifference)
24 input = maximumDifference;
25 else if (input < minimumDifference)
26 input = minimumDifference;
27 for (pointIndex = 0; pointIndex < pointCount; pointIndex++) {
28 if (input > gElement_PowerResistCurve[pointIndex][0])
29 break;
30 }
31 if (pointIndex == pointCount) {
32 multiplier = gElement_PowerResistCurve[pointIndex - 1][1];
33 } else {
34 differenceSpan = gElement_PowerResistCurve[pointIndex - 1][0] -
35 gElement_PowerResistCurve[pointIndex][0];
36 multiplierSpan = gElement_PowerResistCurve[pointIndex - 1][1] -
37 gElement_PowerResistCurve[pointIndex][1];
38 multiplier = (input - gElement_PowerResistCurve[pointIndex][0]) *
39 multiplierSpan / differenceSpan +
40 gElement_PowerResistCurve[pointIndex][1];
41 }
42 switch (halve) {
43 case 0:
44 break;
45 case 1:
46 multiplier /= 2;
47 break;
48 }
49 return multiplier + 0x100;
50}
51
52/// The battle's random number generator, the same linear congruential one as
53/// Random16 with its own seed. Returns bits 8 to 23 of the new seed and writes
54/// gBattleRandomSeed. Rolls that use it advance independently of Random16;
55/// link battles exchange this seed before resolving the round.
57 u32 seed;
58
59 seed = ((u32)gBattleRandomSeed * 0x41c64e6dU) + 0x3039U;
60 gBattleRandomSeed = seed;
61 return (seed << 8) >> 16;
62}
63
64/// Returns a battle random number from 0 to 99.
65u32 BattleRandomPercent(void) { return ((u32)BattleRandom() * 100) >> 16; }
66
67/// Scales max(attack - defense, 0) + twice the action's added power by the
68/// elemental multiplier, then halves it. elementDifference is power minus
69/// resistance; its effect here is half Psynergy's (128..384 with 256 = 1x).
70/// Reached through GAME.S as BattleAtkCalc, which BattleTargetExec calls for
71/// attacks and physical abilities.
72s32 Battle_CalcAttackDamage(s32 attack, s32 defense, s32 addedPower,
73 s32 elementDifference) {
74 s32 damage;
75 s32 scaledDamage;
76
77 attack -= defense;
78 damage = elementDifference;
79 if (attack < 0)
80 attack = 0;
81 scaledDamage =
82 Battle_GetElementMultiplier(damage, 1) * (attack + (addedPower * 2));
83 if (scaledDamage < 0)
84 scaledDamage += 0x1ff;
85 damage = scaledDamage >> 9;
86 if (damage < 0)
87 damage = 0;
88 return damage;
89}
90
91/// Returns the power of Psynergy: `power` times the multiplier for the element
92/// difference (power minus resistance, 0..512 with 256 = 1x) and the supplied
93/// powerMultiplier (also 256 = 1x). Reached through GAME.S as BattlePowerCalc,
94/// which BattleTargetExec calls for Psynergy damage.
95s32 Battle_CalcPsynergyPower(s32 power, s32 elementDifference,
96 s32 powerMultiplier) {
97 s32 scaledPower;
98
99 scaledPower = Battle_GetElementMultiplier(elementDifference, 0) * power *
100 powerMultiplier;
101 if (scaledPower < 0)
102 scaledPower = scaledPower + 0xffff;
103 return scaledPower >> 16;
104}
105
106/// Returns the amount healed: `power` and `powerMultiplier` (256 is 1x) times
107/// the multiplier for twice the healer's element power above 100.
108/// elementPower : the healer's power in the element (100 is 1x)
109/// Reached through GAME.S as BattleRecoveryCalc, which the battle and the
110/// menu's healing use.
111s32 Battle_CalcRecovery(s32 power, s32 elementPower, s32 powerMultiplier) {
112 s32 signedProduct;
113 u32 scaledRecovery;
114
115 scaledRecovery =
116 Battle_GetElementMultiplier(elementPower * 2 - 200, 0) * power;
117 scaledRecovery = powerMultiplier * scaledRecovery;
118 signedProduct = scaledRecovery;
119 if (signedProduct < 0)
120 signedProduct = signedProduct + 0xffff;
121 return signedProduct >> 16;
122}
123
124/// Returns the element the unit attacks with (ELEMENT_NONE for none): an
125/// enemy's comes from its kind, a character's from its equipped weapon.
126/// Called by the battle plan for an attack's element.
128 struct ItemDefinition *item;
129 struct BattleUnit *unit;
130
131 unit = UnitGet(owner);
132 if (unit->classIndex == 0)
133 return Enemy_GetAttackElement(unit);
135 if (item != NULL)
136 return item->element;
137 return ELEMENT_NONE;
138}
139
140/// Returns the unleash chance, in percent, that a unit's equipment adds
141/// (ITEM_EFFECT_ADD_UNLEASH_CHANCE). Called by RollWeaponUnleash and the
142/// battle plan.
144 s32 chanceBonus;
145 s32 inventorySlot;
146 s32 effectIndex;
147 struct ItemDefinition *item;
148
149 chanceBonus = 0;
150 for (inventorySlot = 0; inventorySlot < INVENTORY_SLOTS; inventorySlot++) {
151 if (unit->inventory[inventorySlot] & INVENTORY_EQUIPPED) {
152 item = Item_GetDirect(unit->inventory[inventorySlot]);
153 for (effectIndex = 0; effectIndex < 4; effectIndex++) {
154 if (item->effects[effectIndex].kind == ITEM_EFFECT_ADD_UNLEASH_CHANCE)
155 chanceBonus += item->effects[effectIndex].amount;
156 }
157 }
158 }
159 if (chanceBonus < 0)
160 chanceBonus = 0;
161 return chanceBonus;
162}
163
164/// Rolls whether a character's weapon unleashes: its chance is the weapon's
165/// unleashChance times 5 percent plus what the equipment adds. Returns the
166/// unleash action, or 1 for a plain attack. Called by the battle plan for
167/// each Attack command.
169 struct ItemDefinition *item;
170 s32 rate;
171
172 if (owner->classIndex == 0) {
173 return 1;
174 }
176 if (item == NULL) {
177 return 1;
178 }
179 if (item->unleashAction == 0) {
180 return 1;
181 }
182 rate = ((Equipment_GetUnleashRateBonus(owner) + (item->unleashChance * 5))
183 << 0x10) /
184 100;
185 if (rate > (BattleRandom() & 0xFFFF)) {
186 return item->unleashAction;
187 }
188 return 1;
189}
190
191/// Returns the base chance, in percent, of effect `effectId`. A negative
192/// value is a fixed chance that elements and luck do not change; effects not
193/// listed always work (-100). Called by BattleFx_RollSuccess.
195 s32 fixedChance;
196 u32 effectIndex;
197
198 // The switch is on the effect less 8, as the ROM's jump table is.
199 effectIndex = effectId - 8;
200 switch (effectIndex) {
203 return 70;
206 return 75;
207 case ABILITY_EFFECT_CHARM - 8:
208 return 30;
209 case ABILITY_EFFECT_STUN - 8:
210 return 40;
211 case ABILITY_EFFECT_SLEEP - 8:
212 return 45;
213 case ABILITY_EFFECT_POISON - 8:
215 case ABILITY_EFFECT_SEAL - 8:
216 return 55;
217 case ABILITY_EFFECT_HAUNT - 8:
218 return 25;
220 return 20;
223 return 65;
226 return 35;
228 return 50;
230 fixedChance = 60;
231 break;
233 fixedChance = 90;
234 break;
237 case ABILITY_EFFECT_CURSE - 8:
239 return 60;
240 default:
241 fixedChance = 100;
242 break;
243 }
244 return 0 - fixedChance;
245}
246
247/// Returns 1 when the unit is weak to effect `effectId`: one of three listed
248/// for its enemy kind (EnemyDefinition.unk48) or class
249/// (ClassDefinition.unk50). Called by BattleFx_RollSuccess.
250s32 BattleTarget_IsWeakToEffect(const struct BattleUnit *target, s32 effectId) {
251 const u8 *weakEffects;
252 s32 weaknessIndex;
253
254 if (target->classIndex == 0) {
255 weakEffects = Enemy_GetDefinition(target->classId) + 0x48;
256 for (weaknessIndex = 0; weaknessIndex < 3; weaknessIndex++) {
257 if (*weakEffects == effectId)
258 return 1;
259 weakEffects++;
260 }
261 } else {
262 weakEffects = Class_GetDefinition(target->classIndex)->unk50;
263 for (weaknessIndex = 0; weaknessIndex < 3; weaknessIndex++) {
264 if (*weakEffects++ == effectId)
265 return 1;
266 }
267 }
268 return 0;
269}
270
271/// Returns 1 for the revive effects (Revive, Quartz, Dew), which only work on
272/// a downed unit. Called by the battle's targeting, which lets them aim at
273/// downed members, and by BattleFx_RollSuccess.
275 if ((effectId == ABILITY_EFFECT_REVIVE) ||
276 (effectId == ABILITY_EFFECT_REVIVE_HALF) ||
277 (effectId == ABILITY_EFFECT_REVIVE_80)) {
278 return 1;
279 }
280 return 0;
281}
282
283/// Rolls whether effect `effectId` takes hold on `target`: it fails at once
284/// when there is nothing for it to do. The chance is the base rate, plus three
285/// times the caster's lead over the target in the element, less half the
286/// target's luck, plus 25 against a weakness, times `successScale` percent.
287/// Luff gets three tries. Reached through GAME.S as BattleHitCheck, which
288/// BattleTargetExec and the battle plan call for each target of an ability
289/// with an effect.
290s32 BattleFx_RollSuccess(s32 caster, s32 target, s32 resistanceCategory,
291 s32 effectId, s32 successScale) {
292 /* FAKEMATCH: the psySeal, confusion and haunt reads go through state[offset]
293 * so each builds its address afresh, as native; as unit fields, 120
294 * instructions differ in tbs-en. */
295 u8 *state = (u8 *)UnitGet(target);
296 struct BattleUnit *unit = (struct BattleUnit *)state;
297 s32 attempts = 1;
298 s32 score;
299 s32 attempt;
300 s8 *poison;
301 u8 *delusion;
302 u8 *confusion;
303 u8 *charm;
304 u8 *stun;
305 u8 *sleep;
306
307 if (Battle_IsReviveEffect(effectId) != 0 && unit->hp != 0) {
308 return 0;
309 }
310
311 if (effectId == ABILITY_EFFECT_CURE_POISON && unit->poison == 0) {
312 return 0;
313 }
314
315 // Restore needs a delusion, confusion or charm, or failing those a stun,
316 // sleep, seal or curse; the order of these tests is the ROM's.
317 goto action4_check;
318action4_tail:
319 if (unit->stun == 0 && unit->sleep == 0 &&
320 state[(u32)&((struct BattleUnit *)0)->psySeal] == 0 &&
321 unit->grimReaper == 0) {
322 return 0;
323 }
324 goto action4_done;
325action4_check:
326 if (effectId == ABILITY_EFFECT_RESTORE) {
327 if (unit->delusion == 0 &&
328 state[(u32)&((struct BattleUnit *)0)->confusion] == 0 &&
329 unit->charm == 0) {
330 goto action4_tail;
331 }
332 }
333
334action4_done:
335 poison = &unit->poison;
336 delusion = &unit->delusion;
337 confusion = (u8 *)&unit->confusion;
338 charm = &unit->charm;
339 stun = &unit->stun;
340 sleep = &unit->sleep;
341 if (effectId == ABILITY_EFFECT_CURE_ALL && *poison == 0 && *delusion == 0 &&
342 *confusion == 0 && *charm == 0 && *stun == 0 && *sleep == 0 &&
343 unit->psySeal == 0 && unit->grimReaper == 0 &&
344 state[(u32)&((struct BattleUnit *)0)->haunt] == 0) {
345 return 0;
346 }
347
348 if (effectId == ABILITY_EFFECT_CURSE && unit->grimReaper == 1) {
349 return 0;
350 }
351
352 // A positive base rate is adjusted by the element levels, the target's luck
353 // and its weaknesses; a negative one is a fixed chance.
354 score = BattleFx_GetBaseSuccessRate(effectId);
355 if (score > 0) {
356 s32 difference = Unit_GetElementLevel(caster, resistanceCategory) -
357 Unit_GetElementLevel(target, resistanceCategory) -
358 (unit->luck >> 1);
359 score += difference * 3;
360 if (BattleTarget_IsWeakToEffect(unit, effectId) != 0) {
361 score += 25;
362 }
363 } else {
364 score = -score;
365 }
366
367 if (effectId == ABILITY_EFFECT_SEAL_2_TURNS) {
368 attempts = 3;
369 }
370
371 for (attempt = 0; attempt < attempts; attempt++) {
372 if (score * successScale / 100 >= (s32)BattleRandomPercent()) {
373 return 1;
374 }
375 }
376 return 0;
377}
s32 BattleTarget_IsWeakToEffect(const struct BattleUnit *target, s32 effectId)
Returns 1 when the unit is weak to effect effectId: one of three listed for its enemy kind (EnemyDefi...
s32 RollWeaponUnleash(struct BattleUnit *owner)
Rolls whether a character's weapon unleashes: its chance is the weapon's unleashChance times 5 percen...
s32 Battle_CalcRecovery(s32 power, s32 elementPower, s32 powerMultiplier)
Returns the amount healed: power and powerMultiplier (256 is 1x) times the multiplier for twice the h...
s32 Battle_GetElementMultiplier(s32 input, s32 halve)
Returns the multiplier (256 = 1x) for an element difference input, read from a five-point line (gElem...
Definition battle_calc.c:11
s32 Item_GetEquippedElement(s32 owner)
Returns the element the unit attacks with (ELEMENT_NONE for none): an enemy's comes from its kind,...
s32 Battle_IsReviveEffect(s32 effectId)
Returns 1 for the revive effects (Revive, Quartz, Dew), which only work on a downed unit.
s32 Battle_CalcAttackDamage(s32 attack, s32 defense, s32 addedPower, s32 elementDifference)
Scales max(attack - defense, 0) + twice the action's added power by the elemental multiplier,...
Definition battle_calc.c:72
s32 BattleFx_GetBaseSuccessRate(s32 effectId)
Returns the base chance, in percent, of effect effectId.
s32 BattleRandom(void)
The battle's random number generator, the same linear congruential one as Random16 with its own seed.
Definition battle_calc.c:56
s32 BattleFx_RollSuccess(s32 caster, s32 target, s32 resistanceCategory, s32 effectId, s32 successScale)
Rolls whether effect effectId takes hold on target: it fails at once when there is nothing for it to ...
s32 Battle_CalcPsynergyPower(s32 power, s32 elementDifference, s32 powerMultiplier)
Returns the power of Psynergy: power times the multiplier for the element difference (power minus res...
Definition battle_calc.c:95
s32 Equipment_GetUnleashRateBonus(struct BattleUnit *unit)
Returns the unleash chance, in percent, that a unit's equipment adds (ITEM_EFFECT_ADD_UNLEASH_CHANCE)...
u32 BattleRandomPercent(void)
Returns a battle random number from 0 to 99.
Definition battle_calc.c:65
s32 gBattleRandomSeed
@ ELEMENT_NONE
Definition battle_unit.h:40
@ 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_ATTACK_DOWN
Blunt.
Definition battle_unit.h:85
@ ABILITY_EFFECT_POISON
Taint.
Definition battle_unit.h:94
@ ABILITY_EFFECT_HALVE_DEFENSE
Torch.
@ ABILITY_EFFECT_SEAL
Bind: Psynergy sealed.
@ ABILITY_EFFECT_CURE_ALL
Tonic.
@ 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_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_CURSE
Curse: the Grim Reaper's countdown.
@ ABILITY_EFFECT_RESISTANCE_DOWN
Enfeeble.
Definition battle_unit.h:93
@ ABILITY_EFFECT_REVIVE
Revive, Water of Life: full HP.
Definition battle_unit.h:81
@ ABILITY_EFFECT_DELUSION
Delude.
Definition battle_unit.h:96
@ ABILITY_EFFECT_DRAIN_PP
Psy Drain.
@ ABILITY_EFFECT_CONFUSION
Confuse.
Definition battle_unit.h:97
@ ABILITY_EFFECT_DEADLY_POISON
Poison.
Definition battle_unit.h:95
@ ABILITY_EFFECT_CURE_POISON
Cure Poison, Antidote.
Definition battle_unit.h:79
@ ABILITY_EFFECT_DEFENSE_DOWN_2
Impair.
Definition battle_unit.h:88
struct ClassDefinition * Class_GetDefinition(s32 classId)
Returns class classId's definition: its series, element requirements, stat multipliers and the Psyner...
Definition classes.c:223
s32 Enemy_GetAttackElement(struct BattleUnit *unit)
Returns the element an enemy attacks with, from its element preset.
Definition classes.c:82
s32 Unit_GetElementLevel(s32 owner, s32 element)
Returns a unit's level in element (0 to 3).
Definition classes.c:67
const s16 gElement_PowerResistCurve[5][2]
What every part of the game object shares: its includes and the inventory helper two parts use.
struct ItemDefinition * Inventory_GetEquippedDefinition(struct BattleUnit *unit, s32 type)
Returns the definition of the unit's equipped item of type, or NULL.
Definition inventory.c:315
struct ItemDefinition * Item_GetDirect(s32 item)
Returns item item's definition; an inventory entry may be passed, its equipped and quantity bits are ...
Definition inventory.c:10
@ ITEM_TYPE_WEAPON
swords, axes, maces, rods and staffs
Definition item.h:46
@ ITEM_EFFECT_ADD_UNLEASH_CHANCE
percent added to the weapon's unleash
Definition item.h:83
@ INVENTORY_EQUIPPED
Definition item.h:17
@ INVENTORY_SLOTS
Definition item.h:23
#define NULL
Definition bilibin.h:6
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 luck
0 to 99 (UnitCalc)
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).
s8 poison
1 poisoned, 2 deadly poison
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).
u8 classIndex
The character's class, worked out from its Djinn (Unit_UpdateClass); 0 marks an enemy,...
u16 inventory[15]
Items held, one entry each: bits 15..11 quantity | 10 broken | 9 equipped | 8..0 item id (INVENTORY_*...
u8 delusion
Delusion to sleep: the turns each status has left, 7 when it lands; each may end sooner at the end of...
u8 unk50[4]
[0] to [2]: the ability effects the class is weak to (BattleTarget_IsWeakToEffect); [3] is not read t...
Definition character.h:67
An item's definition: its price, type, who can equip it, its bonuses and unleash, its effects and the...
Definition item.h:104
s32 element
a weapon's attack element; 4 for none
Definition item.h:118
u16 unleashAction
the action a weapon unleashes, or 0
Definition item.h:115
struct ItemEffect effects[4]
what it does while equipped
Definition item.h:119
u8 unleashChance
in steps of 5 percent
Definition item.h:112
u8 kind
enum ItemEffectKind; 0 for none
Definition item.h:93
s8 amount
what the kind adds or scales by
Definition item.h:94
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
unsigned char u8
Definition types.h:11
const u8 * Enemy_GetDefinition(s32 selector)
Returns the definition of enemy selector - 8; an id past the first 250 gives the first.
Definition units.c:103
struct BattleUnit * UnitGet(u32 owner)
Returns the unit record of owner: 0 to 7 are the playable characters in the game state,...
Definition units.c:73