Alchemy
Loading...
Searching...
No Matches

enemy_ai.c

Go to the documentation of this file.
1/// @file
2/// An enemy's choice of action, BattleCommand_SelectAutomatic.
3/// Part of battle.c, the battle object, compiled only through it.
4
6
7/// An enemy's memory of its action choice, kept in BattleUnit.rewards (0 in
8/// every row of enemies.tsv, so it starts cleared). Only action strategies 3
9/// and 4 use it.
10/// bits: 31..4 unused | 3..1 selection | 0 initialized
12 unsigned initialized : 1; ///< strategy 3 has drawn its random start
13 unsigned selection : 3; ///< the place in its action list, 0 to 7
14 unsigned remaining : 28; ///< not used here
15};
16
17/// Chooses an enemy's action (a party member's is chosen in the menu): one of
18/// the eight in its definition, picked by its action strategy, with an item
19/// instead where its item mask says so, and a target worth the ability. A
20/// choice that cannot be made (too little PP, sealed, a forbidden escape, no
21/// worthwhile target) picks again; after 18 tries the enemy defends.
22/// Called with retry 0 for each enemy as the round is built
23/// (BattlePres_BuildOpponentEntries), and with retry 1 at its turn
24/// (BattlePresentation_DispatchAction). Writes request's command, parameter,
25/// target and range; strategies 3 and 4 advance the place remembered in
26/// BattleUnit.rewards. Returns nothing.
27/// retry : 0 when the round is built: only a guard is settled now (it must
28/// raise before anyone acts); 1 at the enemy's turn: the rest.
29#ifdef UBFIX
31 s32 retry) {
32#else
34 s32 retry) {
35#endif
36 u32 selection;
37 struct BattleAutomaticState *selectionMemory;
38 struct EnemyDefinition *enemy;
39 s32 itemsAllowed;
40 s32 failedChoices;
41 s32 needsChoice;
43 u8 *itemActionMask;
44 s16 command;
45 s32 allyTarget;
46 s32 opponentTarget;
47 s32 allyIsParty;
48 s32 opponentIsParty;
49 struct ItemDefinition *item;
50 s32 useItem;
51 s8 strategyKind;
52 struct BattleAct *itemAction;
53 struct BattleAct *ability;
54 struct BattleUnit *unit;
55 s32 abilityId;
56 s32 flags;
57 u8 effect;
58 u8 targetMode;
59
60 /// Picks a place in the action list from a 0-255 roll and eight weights:
61 /// the first place whose running total passes the roll, else place 0.
62 s32 BattleEnemy_SelectWeightedAction(u8 *weights) {
63 s32 roll;
64 s32 total;
65 s32 selectedAction;
66 s32 actionIndex;
67
68 roll = BattleRandomFar() & 0xff;
69 total = weights[0];
70 selectedAction = 0;
71 actionIndex = 0;
72 if (roll >= total) {
73 while (++actionIndex <= 7) {
74 total += weights[actionIndex];
75 if (roll < total) {
76 selectedAction = actionIndex;
77 break;
78 }
79 }
80 }
81 return selectedAction;
82 }
83
84 unit = UnitGetFar(request->unitId);
85 needsChoice = itemsAllowed = 1;
86 failedChoices = 0;
87 // Strategy 5's first pick is place 0. No row of enemies.tsv uses strategies
88 // 4 to 6 (only 0, 1 and 3); strategy 6 would keep this -1 as its place.
89 selection = -1U;
90 // A party member chooses in the menu.
91 if (unit->classIndex != 0)
92 return;
93 if (retry != 0 && request->command != CMD_ABILITY)
94 return;
95
96 enemy = (struct EnemyDefinition *)Enemy_GetDefinitionFar(unit->classId);
97 selectionMemory = (struct BattleAutomaticState *)&unit->rewards;
98 actionStrategy = &enemy->actionStrategy;
99 itemActionMask = &enemy->itemActions;
100
101 // A place in the action list, by the enemy's strategy.
102select_again:
103 strategyKind = *actionStrategy;
104 switch (strategyKind) {
105 // Strategies 0 to 2: one of the three weight tables.
106 case 0:
107 selection =
108 BattleEnemy_SelectWeightedAction(gBattleCommand_SelectionWeights[0]);
109 break;
110 case 1:
111 selection =
112 BattleEnemy_SelectWeightedAction(gBattleCommand_SelectionWeights[1]);
113 break;
114 case 2:
115 selection =
116 BattleEnemy_SelectWeightedAction(gBattleCommand_SelectionWeights[2]);
117 break;
118 case 3: // from a random place, then in order
119 if (selectionMemory->initialized == 0) {
120 selectionMemory->selection = BattleRandomFar();
121 selectionMemory->initialized = 1;
122 }
123 // fall through
124 case 4: // in order from the remembered place
125 // It moves on only at the enemy's turn, wrapping from 7 to 0 in its three
126 // bits.
127 selection = selectionMemory->selection;
128 if (retry != 0) {
129 selectionMemory->selection = selection + 1;
130 }
131 break;
132 case 5: // the next place at each new pick
133 selection++;
134 break;
135 case 6: // the same place
136 break;
137 }
138
139 // A place marked for an item: use the first item held, if any. Once an
140 // item is refused, later picks take the place's ability instead.
141 useItem = (*itemActionMask >> selection) & 1;
142 useItem &= itemsAllowed;
143 abilityId = enemy->actions[selection];
144 request->command = CMD_ABILITY;
145 if (useItem != 0 && retry != 0) {
146 if (!(ITEM_ID_MASK & unit->inventory[0])) {
147 useItem = 0;
148 if (enemy->targetStrategy == 0) {
149 request->command = CMD_ITEM;
150 // XXX: 0x1fd (509) is the row of the ability Can't Use, yet CMD_ITEM
151 // reads its parameter as an inventory slot (BattleCommand_BuildPlan);
152 // a Brigand or Rat Fighter (target strategy 0, item places) out of
153 // items would show what the player sees.
154 request->parameter = 0x1fd;
155 return;
156 }
157 }
158 if (useItem != 0) {
159 item = Item_GetDirectFar(unit->inventory[0]);
160 // Only an item that is used up, whose ability's flags are exactly kind
161 // 1 (heal HP) or 2 (an effect), with no higher bit set.
162 if (item->useType == ITEM_USE_CONSUMED) {
163 itemAction = BattleActGet(item->actionId);
164 abilityId = item->actionId;
165 request->command = CMD_ITEM;
166 flags = itemAction->flags;
167 request->parameter = 0;
168 switch (flags) {
169 case 1:
170 case 2:
171 break;
172 default:
173 useItem = 0;
174 break;
175 }
176 } else {
177 useItem = 0;
178 }
179 }
180 if (useItem == 0)
181 itemsAllowed = 0;
182 }
183
184 if (needsChoice != 0) {
185 // A guard or an escape targets the user itself.
186 ability = BattleActGet(abilityId);
187 effect = ability->effect;
188 switch (effect) {
190 request->command = CMD_DEFEND;
191 request->target = Battle_FindTaggedSlotByValue(request->unitId);
192 break;
194 request->command = CMD_DEFEND2;
195 request->target = Battle_FindTaggedSlotByValue(request->unitId);
196 break;
198 request->command = CMD_FLEE;
199 request->target = Battle_FindTaggedSlotByValue(request->unitId);
200 break;
201 }
202
203 // As the round is built, only a guard is kept; anything else waits for
204 // the enemy's turn.
205 if (retry == 0) {
206 switch (request->command) {
207 case CMD_DEFEND:
208 case CMD_DEFEND2:
209 break;
210 default:
211 goto done;
212 }
213 }
214
215 // Psynergy: a careful enemy (strategy not 0) avoids it without the PP,
216 // and the most careful (2) also while sealed.
217 if (request->command != CMD_ITEM && Ability_IsPsynergy(abilityId) != 0) {
218 request->command = CMD_PSYNERGY;
219 request->parameter = abilityId;
220 if (ability->ppCost > unit->pp && enemy->targetStrategy != 0)
221 goto next_candidate;
222 if (unit->psySeal != 0 && enemy->targetStrategy == 2)
223 goto next_candidate;
224 }
225 // A challenged enemy won't flee, and at its turn it no longer chooses a
226 // guard (guards are settled as the round is built): both pick again.
227 command = request->command;
228 if (command == CMD_FLEE && (u8)unit->challenge != 0)
229 goto next_candidate;
230 if (retry != 0 && (command == CMD_DEFEND || command == CMD_DEFEND2))
231 goto next_candidate;
232
233 if (command == CMD_ABILITY) {
234 request->parameter = abilityId;
235 if (abilityId == ABILITY_ATTACK)
236 request->command = CMD_ATTACK;
237 }
238
239 // The target. With no one listed on the side (-2) a random place is
240 // taken instead, on the actor's own side for both modes (allyIsParty and
241 // opponentIsParty both test the actor); with no one worth it (-1) the
242 // enemy picks again. Mode 3 and the others target the user, but mode 3
243 // leaves the choice open, so the enemy picks again after it too.
244 request->range = ability->range;
245 targetMode = ability->targetMode;
246 switch (targetMode) {
248 case TARGET_MODE_SELF:
249 allyTarget = BattleTarget_SelectForAction(request->unitId, ability);
250 if (allyTarget == -2) {
251 allyIsParty = 0;
252 if (request->unitId <= (u32)ALLY_MAX)
253 allyIsParty = 1;
254 allyTarget = BattleTarget_SelectRandomPosition(allyIsParty);
255 }
256 if (allyTarget != -1) {
257 request->target = allyTarget;
258 needsChoice = 0;
259 }
260 break;
261 case TARGET_MODE_FOES:
262 opponentTarget = BattleTarget_SelectForAction(request->unitId, ability);
263 if (opponentTarget == -2) {
264 opponentIsParty = 0;
265 if (request->unitId <= (u32)ALLY_MAX)
266 opponentIsParty = 1;
267 opponentTarget = BattleTarget_SelectRandomPosition(opponentIsParty);
268 }
269 if (opponentTarget != -1) {
270 request->target = opponentTarget;
271 needsChoice = 0;
272 }
273 break;
274 case 3:
275 request->target = Battle_FindTaggedSlotByValue(request->unitId);
276 break;
277 default:
278 opponentTarget = Battle_FindTaggedSlotByValue(request->unitId);
279 request->target = opponentTarget;
280 needsChoice = 0;
281 break;
282 }
283 }
284 // As the round is built, one pick is all.
285 if (retry == 0)
286 needsChoice = 0;
287next_candidate:
288 // On the 18th failed pick, defend.
289 if (needsChoice != 0 && failedChoices > 16) {
290 request->command = CMD_DEFEND;
291 needsChoice = 0;
292 }
293 failedChoices++;
294 if (needsChoice != 0)
295 goto select_again;
296done:
297 return;
298}
u8 gBattleCommand_SelectionWeights[3][8]
What every part of the battle object shares: its includes, in their original order,...
#define ITEM_USE_CONSUMED
one is used up
#define TARGET_MODE_FOES
#define ALLY_MAX
the last party member's unit id
#define ABILITY_ATTACK
the ability "Attack"
#define TARGET_MODE_ALLIES
#define TARGET_MODE_SELF
@ CMD_ABILITY
an enemy's ability
@ CMD_DEFEND
@ CMD_ITEM
@ CMD_PSYNERGY
@ CMD_ATTACK
@ CMD_FLEE
@ CMD_DEFEND2
an enemy's Total Defense (effect 47)
@ ABILITY_EFFECT_TOTAL_DEFENSE
Total Defense, Flash.
@ ABILITY_EFFECT_DEFEND
Defend, Granite.
@ ABILITY_EFFECT_FLEE
Flee.
s32 BattleCommand_SelectAutomatic(struct BattleActionRecord *request, s32 retry)
Chooses an enemy's action (a party member's is chosen in the menu): one of the eight in its definitio...
Definition enemy_ai.c:33
struct BattleUnit * UnitGetFar(u32 owner)
struct ItemDefinition * Item_GetDirectFar(s32 item)
struct BattleAct * BattleActGet(s32 actionId)
Far call to BattleAction_GetDirect.
const u8 * Enemy_GetDefinitionFar(s32 selector)
s32 BattleRandomFar(void)
@ ITEM_ID_MASK
Definition item.h:16
s32 Battle_FindTaggedSlotByValue(u32 unitId)
Returns the list place of unit id, or -1 if it is in neither list: the party's as 0x100 | place,...
Definition round.c:724
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 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's chosen action for the turn: its command, the command's parameter, its target and range,...
s16 target
the aimed unit's id
s16 parameter
an ability id, or CMD_ITEM's inventory index (-1 gone)
s16 command
enum BattleCommand
s16 range
the action's range
An enemy's memory of its action choice, kept in BattleUnit.rewards (0 in every row of enemies....
Definition enemy_ai.c:11
unsigned selection
the place in its action list, 0 to 7
Definition enemy_ai.c:13
unsigned remaining
not used here
Definition enemy_ai.c:14
unsigned initialized
strategy 3 has drawn its random start
Definition enemy_ai.c:12
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u32 rewards
Copied from the enemy's definition, where it is 0 in every row.
u8 psySeal
bits: 7..3 Luff (16 when cast, minus 8 a turn) | 2..0 Bind's turns (7 when cast).
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).
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_*...
An enemy's definition: its level and stats, items, element preset, the actions it chooses from and it...
Definition character.h:89
u8 itemActions
one bit per place in actions: use an item held instead
Definition character.h:121
s8 targetStrategy
Whom a single-target attack favours: the target with the most HP left (0), the most maximum HP (1) or...
Definition character.h:116
s8 actionStrategy
How the action is chosen from actions (BattleCommand_SelectAutomatic): one of three weight tables (0 ...
Definition character.h:120
u16 actions[8]
the abilities the enemy chooses from
Definition character.h:122
An item's definition: its price, type, who can equip it, its bonuses and unleash, its effects and the...
Definition item.h:104
u8 useType
what using it costs (ITEM_USE_), or 0 for nothing
Definition item.h:113
u16 actionId
the ability used, or taught as Psynergy
Definition item.h:120
u32 Ability_IsPsynergy(s32 abilityId)
Returns 1 for an ability that costs PP, and for Psy Drain, which costs none.
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 char s8
Definition types.h:10
signed short s16
Definition types.h:12
unsigned char u8
Definition types.h:11