Alchemy
Loading...
Searching...
No Matches

encounter.c

Go to the documentation of this file.
1/// @file
2/// Random encounters, and each scene's music, place name and backdrop.
3/// Part of object.c, the field object, compiled only through it.
4
6
7/// A field scene's encounter zones (gBattle_LocationRules, location_rules.s),
8/// ended by scene -1.
11 s16 entrance; ///< -1 for any entrance
12 s16 flag : 15; ///< -1, or a flag that turns the rule off once set
13 s16 firstZoneOnGround : 1; ///< the first zone is for ground 0, not 1
14 s16 firstZone; ///< the first of up to seven consecutive zones
15};
16
17extern const struct BattleLocationRule gBattle_LocationRules[];
18
19/// The world map's encounter zone for each kind of terrain
20/// (gWorldMap_EncounterRules, zonedata.c), ended by kind -1.
21typedef struct TerrainRule {
22 s16 kind; ///< the terrain kind of the map block
23 s16 behavior; ///< the terrain's behavior, or -1 for any
24 s16 flag; ///< -1, or a flag that turns the rule off once set
27
28/// The number of enemy groups an encounter zone can hold.
30
31/// An encounter zone, a row of encounter.tsv: its rate, the level it is made
32/// for, and the enemy groups that can appear with their chances.
34 u16 rate; ///< the base encounter rate
35 u16 level; ///< the party level the zone is made for
36 u16 enemies[ENCOUNTER_GROUP_COUNT]; ///< the enemy groups that can appear
37 u8 weights[ENCOUNTER_GROUP_COUNT]; ///< each group's relative chance
38};
39
40/// Chooses the current scene's encounter zones, one for each kind of ground
41/// the party can stand on (gEventWork->encounterGroups, read by
42/// EffectRuntime_LookupByTableEntry), from the first rule matching the scene
43/// and entrance. Ground 0 has none, and grounds 1 to 7 take consecutive zones
44/// from the rule's first one. With useRules 0, or no matching rule, no ground
45/// has encounters. Also resets the encounter counters and chooses the battle
46/// backdrop. Called by Field_RunScene on scene load, with useRules 0 on the
47/// world map and 1 in a field scene; writes work->encounterGroups,
48/// encounterBias and encounterThreshold, and gGameState.special.
50 struct FieldWork *work;
51 s32 scene;
52 s32 entrance;
53 u8 *groundZones;
54 const struct BattleLocationRule *rule;
55 s32 zone;
57 u32 i;
58
59 work = (struct FieldWork *)gEventWork;
60 scene = gGameState.scene;
61 entrance = gGameState.entrance;
62 groundZones = work->encounterGroups;
64 zone = 0;
65 firstZoneOnGround = 0;
66 if (useRules != 0) {
67 while (rule->scene != -1) {
68 if (rule->scene == scene &&
69 (rule->entrance == -1 || rule->entrance == entrance)) {
70 if (rule->flag == -1 || GameFlag_TestFar(rule->flag) == 0) {
71 zone = rule->firstZone;
72 firstZoneOnGround = rule->firstZoneOnGround;
73 break;
74 }
75 }
76 rule++;
77 }
78 }
79
80 // Ground zero is normally safe; the rule can move the first zone onto it.
81 *groundZones++ = 0;
82 for (i = 0; i <= 6; i++) {
83 *groundZones++ = zone;
84 if (zone != 0)
85 zone++;
86 }
87 if (firstZoneOnGround != 0) {
88 work->encounterGroups[0] = work->encounterGroups[1];
89 work->encounterGroups[1] = 0;
90 }
91 work->encounterBias = 0;
92 // 16 in 16.16: Encounter_SelectEnemyGroup starts a battle once
93 // gGameState.encounterSteps reaches it.
94 work->encounterThreshold = 16 * Q16_ONE;
96}
97
98/// Returns the world map encounter zone for the terrain at pos (0 for none),
99/// and chooses the battle backdrop for that terrain (gGameState.special).
100/// Called by Encounter_RollWorldMap on each step, and by
101/// Field_RequestBattleFromEncounterTable and Field_RequestDjinnBattle on the
102/// world map, which use only the backdrop.
104 s32 kind;
106 s32 behavior = GetWorldMapTerrainBehaviorFar(pos, &kind);
107 s32 zone = 0;
108
109 while (rule->kind != -1) {
110 if (rule->kind == kind &&
111 (rule->behavior == -1 || rule->behavior == behavior) &&
112 (rule->flag == -1 || GameFlag_TestFar(rule->flag) == 0)) {
113 zone = rule->zone;
114 break;
115 }
116 rule++;
117 }
119 return zone;
120}
121
122/// Advances the encounter counter for zone and returns the enemy group to
123/// fight, or 0 for no battle. steps (16.16) scales how far the counter moves;
124/// a battle starts once it reaches encounterThreshold. FLAG_NO_ENCOUNTERS,
125/// flag 353 and gGameState.cloaked prevent battles. Flag 351 skips the checks
126/// and the counting and draws a group at once.
127/// Called by Encounter_RollTerrain and Encounter_RollWorldMap; adds to
128/// gGameState.encounterSteps, sets or clears work->encounterBias, and on a
129/// battle sets gGameState.sceneCue.
131 struct FieldWork *work;
132 struct EncounterZone *entry;
133 s32 rate;
134 s32 level;
135 s32 bias;
136 s32 step;
137 s32 total;
138 s32 sum;
139 s32 pick;
140 s32 i;
141
142 work = (struct FieldWork *)gEventWork;
143 // UB: flag 351 reaches the draw without setting entry. Hoisting the zone
144 // lookup changes native instructions at the same 440-byte size in tbs-en.
145#ifdef UBFIX
146 entry = (struct EncounterZone *)gEncounter_EnemyGroupTable + zone;
147#endif
148 if (GameFlag_TestFar(351) == 0) {
150 return 0;
151 if (zone == 0)
152 return 0;
153 if (gGameState.cloaked != 0)
154 return 0;
155 entry = (struct EncounterZone *)gEncounter_EnemyGroupTable + zone;
156 rate = entry->rate;
157 if (rate == 0)
158 return 0;
159 // With flag 5 set, there are no encounters while unit 5 has more than 130
160 // experience points.
161 if (GameFlag_TestFar(5) != 0) {
163 if (level > 130)
164 return 0;
165 }
166
167 // Each level (up to five) the party's average is above the zone's raises
168 // the rate by 5; with Avoid active such a zone has no encounters at all.
170 CLAMP(level, 0, 5);
171 if (level > 0 && gGameState.avoidDistance != 0)
172 return 0;
173 rate += level * 5;
174
175 // A random bias, the sum of four random numbers, varies the distance
176 // between battles; it is drawn again after each battle.
177 bias = work->encounterBias;
178 if (bias == 0) {
179 s32 a = Random16();
180 s32 b = Random16();
181 s32 c = Random16();
182
183 bias = (a - b + c - (s32)Random16()) / 2;
184 work->encounterBias = bias;
185 }
186 // The step grows with the rate and the bias. The counter is not cleared
187 // here: Field_RunScene clears it when it starts the battle.
188 step =
189 Iwram_RatioMulQ14((rate << 20) + ((rate << 4) - 16) * bias, 0x100000);
190 total = gGameState.encounterSteps += Iwram_MulQ16(step, steps);
191 if (total < work->encounterThreshold)
192 return 0;
193 }
194
195 // Draw one of the eight groups by weight.
196 work->encounterBias = 0;
197 sum = 0;
198 for (i = 0; i < ENCOUNTER_GROUP_COUNT; i++)
199 sum += entry->weights[i];
200 // Zone zero stays encounter-free even when flag 351 bypasses the rate checks.
201 if (sum == 0)
202 return 0;
203 // Random16 is below 65536, so this draw is below sum and must stop in 0..7.
204 pick = (Random16() * sum) >> 16;
205 pick -= entry->weights[0];
206 i = 0;
207 if (pick >= 0) {
208 do {
209 i++;
210 if (i >= ENCOUNTER_GROUP_COUNT)
211 break;
212 pick -= entry->weights[i];
213 } while (pick >= 0);
214 }
215 sum = entry->enemies[i];
217 return sum;
218}
219
220/// Rolls a random battle on the encounter zone of the ground underfoot.
221/// terrain: the terrain number, 0 for ordinary ground.
222/// steps: the step weight, passed on to Encounter_SelectEnemyGroup.
223/// Returns the enemy group to fight, or 0 for no battle.
224/// Called by Field_ProcessStep in a field scene, which stores the result in
225/// work->battle; steps is the leader's speed limit, doubled with FLAG_LURE_CAP.
227 u8 *zone;
228
229 zone = ((struct FieldWork *)gEventWork)->encounterGroups;
230 return Encounter_SelectEnemyGroup(zone[terrain], steps);
231}
232
233/// Rolls a random battle on the world map, taking the zone from the tile at
234/// pos. Returns the enemy group to fight, or 0 for no battle. Called by
235/// Field_ProcessStep on the world map.
238 Encounter_ResolveWorldMapZoneAndBackdrop((void *)pos), steps);
239}
240
241/// Returns the enemy group in slot (0 to 7) of zone; 14 halfwords make a zone.
242/// Called by Field_RequestBattleFromEncounterTable,
243/// Encounter_GetDjinnEnemyGroup and the scene event runner (events.c, zone 99).
245 // A zone's halfwords are rate, level, then the eight groups.
246 return gEncounter_EnemyGroupTable[zone * 14 + slot + 2];
247}
248
249/// Returns the enemy group of Djinni battle djinni, which the table gives as
250/// a zone and slot. Called by Field_RequestDjinnBattle.
252 u16 *zoneAndSlot;
253
254 zoneAndSlot = (u16 *)&gEncounter_AreaEntryTable[djinni * 2];
255#ifdef UBFIX
256 return Encounter_GetEnemyGroupAtSlot(zoneAndSlot[0], zoneAndSlot[1]);
257#else
258 // UB: no return statement; the result of the call is left in r0.
259 Encounter_GetEnemyGroupAtSlot(zoneAndSlot[0], zoneAndSlot[1]);
260#endif
261}
262
263/// A rule choosing a scene's field music (gScene_InteractionRuleTable,
264/// scene_rules.s), ended by id -1.
266 s16 id; ///< a scene, or a scene group
267 s16 entrance : 15; ///< -1 for any entrance
268 u16 idIsScene : 1; ///< 1 when id is a scene, 0 when it is a group
269 s16 flag; ///< -1, or a flag that must be set
271};
272
274
275/// Chooses the current scene's field music (gGameState.cue) from the first rule
276/// matching the scene or its group and the entrance; MUS_018 when none does.
277/// Called by Game_ResetForNewGame on entering each field scene, before
278/// Audio_PlayCueFromEventWork plays it.
280 s16 music = MUS_018;
281 s16 scene = gGameState.scene;
283 s16 group = gGameState.sceneGroup;
285
286 for (; rule->id != -1; rule++) {
287 if (rule->idIsScene) {
288 if (rule->id != scene)
289 continue;
290 if (rule->entrance != -1 && rule->entrance != entrance)
291 continue;
292 if (rule->flag != -1 && GameFlag_TestFar(rule->flag) == 0)
293 continue;
294 music = rule->music;
295 break;
296 }
297 if (rule->id != group)
298 continue;
299 if (rule->entrance != -1 && rule->entrance != entrance)
300 continue;
301 if (rule->flag != -1 && GameFlag_TestFar(rule->flag) == 0)
302 continue;
303 music = rule->music;
304 break;
305 }
306
308}
309
310/// A rule naming the place a scene is in (gScene_AreaNameRules, scene_rules.s),
311/// ended by id -1.
313 s16 id; ///< a scene, or a scene group
314 s16 entrance : 15; ///< -1 for any entrance
315 u16 useSceneId : 1; ///< 1 when id is a scene, 0 when it is a group
316 s32 areaNameIndex; ///< the place name
317};
318
319extern const struct SceneAreaNameRule gScene_AreaNameRules[];
320
321/// Returns the place name of scene and entrance, from the first rule matching
322/// the scene or its group; 0 when none does. Called by the save, debug and
323/// location windows through Scene_GetAreaNameIndexFar, and by
324/// Map_UpdateWorldMapMarkers; most add it to MsgDebugEntryName for the
325/// message. The save stores it as its area (save_state.h).
327 const struct SceneAreaNameRule *rule = gScene_AreaNameRules;
328 s32 name = 0;
329 s32 group = BattleFx_GetResourceGroup(scene);
330
331 while (rule->id != -1) {
332 if (rule->useSceneId) {
333 if (rule->id == scene) {
334 if (rule->entrance == -1 || rule->entrance == entrance) {
335 name = rule->areaNameIndex;
336 break;
337 }
338 }
339 } else {
340 if (rule->id == group) {
341 if (rule->entrance == -1 || rule->entrance == entrance) {
342 name = rule->areaNameIndex;
343 break;
344 }
345 }
346 }
347 rule++;
348 }
349 return name;
350}
351
352/// Sets the scene the party returns to after a defeat (gGameState.savedScene
353/// and savedEntrance), from the first rule of gParty_PairResolveRules that
354/// matches the current scene and entrance: {scene, entrance (-1 for any),
355/// return scene, return entrance}, ended by scene -1. Without a match the
356/// return point stays as it was. Called by Field_RunScene on scene load.
358 s16 scene = gGameState.scene;
360 const s16 *rule = gParty_PairResolveRules;
361
362 while (rule[0] != -1) {
363 if (rule[0] == scene && (rule[1] == -1 || rule[1] == entrance)) {
364 gGameState.savedScene = rule[2];
365 gGameState.savedEntrance = rule[3];
366 return;
367 }
368 rule += 4;
369 }
370}
371
372/// Returns the battle backdrop chosen for the current scene. The battle setup
373/// (battle/encounter.c) reads it through Event_GetSpecialValueFar as the
374/// battle's background.
376
377/// Chooses the battle backdrop for the current scene. gBattle_BackdropScenes
378/// lists each backdrop (a word with bit 31 set) followed by the scenes that
379/// use it, ended by 0; a scene takes the backdrop listed last before it, and a
380/// scene not listed takes the last backdrop. Called by
381/// Encounter_SelectSceneZones; sets gGameState.special.
383 s32 target = gGameState.scene;
385 s32 entry = *table++;
386 // The first scene_rules.s word is a backdrop marker. It cannot match the
387 // halfword scene id, so the loop sets this before testing any scene.
388 s32 backdrop;
389
390 if (entry != 0 && entry != target) {
391 do {
392 if (entry & 0x80000000) {
393 backdrop = entry & 0xFFFF;
394 }
395 entry = *table++;
396 } while (entry != 0 && entry != target);
397 }
398 gGameState.special = backdrop;
399}
400
401/// Chooses the battle backdrop for a world map terrain behavior (1 to 7; any
402/// other gives Sol Sanctum's) and sets gGameState.special. Called by
403/// Encounter_ResolveWorldMapZoneAndBackdrop.
405 u16 backdrop;
406
407 switch (behavior - 1) {
408 case 0:
410 break;
411 case 1:
412 backdrop = (u32)&ResourceId_ForestBackdrop;
413 break;
414 case 2:
415 backdrop = (u32)&ResourceId_DesertBackdrop;
416 break;
417 case 3:
418 case 6:
419 backdrop = (u32)&ResourceId_BeachBackdrop;
420 break;
421 case 4:
422 case 5:
424 break;
425 default:
427 break;
428 }
429 gGameState.special = backdrop;
430}
431
432/// Chooses the battle music for slot of zone (gGameState.sceneCue).
433/// gEncounter_BattleMusic lists each song (with bit 15 set) followed by the
434/// (zone << 4) + slot keys that use it, ended by 0; a key takes the song listed
435/// last before it. Link battles always use MUS_018. Called by
436/// Encounter_SelectEnemyGroup for a random battle, by the battle requests
437/// (Field_RequestBattleFromEncounterTable, Field_RequestDjinnBattle with
438/// zone 0) and by the scene event runner (events.c).
440 u16 key = (zone << 4) + slot;
441 const u16 *table = gEncounter_BattleMusic;
442 // Encounter keys have bit 15 clear. battle_cue.c starts with a nonzero
443 // song marker, so the loop assigns music before either stopping test holds.
444 u16 music;
445 u16 value;
446
448 music = MUS_018;
449 } else {
450 for (;;) {
451 value = *table++;
452 if (value == 0)
453 break;
454 if (value == key)
455 break;
456 if (value & 0x8000)
457 music = value & 0xfff;
458 }
459 }
460 gGameState.sceneCue = music;
461}
s32 gBattle_BackdropScenes[]
u16 gEncounter_EnemyGroupTable[]
const s16 gParty_PairResolveRules[]
const u16 gEncounter_BattleMusic[35]
Battle music: each song as 0x8000 | song, followed by the encounters that use it as (zone << 4) + slo...
Definition battle_cue.c:6
s32 GetWorldMapTerrainBehaviorFar(struct WorldPosition *position, s32 *terrain_kind)
struct BattleUnit * UnitGetFar(u32 owner)
s32 GameFlag_TestFar(s32 flag)
u32 Party_GetAverageLevelFar(void)
#define Q16_ONE
One in the engine's signed 16.16 fixed-point format.
Definition fixed_math.h:10
#define FLAG_NO_ENCOUNTERS
No random encounters while set (Encounter_SelectEnemyGroup), unless flag 0x15f is set; debug entrance...
Definition flags.h:59
#define FLAG_LINK_BATTLE
Definition flags.h:68
struct GameState gGameState
static s32 Iwram_RatioMulQ14(s32 divisor, s32 value)
All calls go through this layer.
Definition iwram_call.h:51
#define Iwram_MulQ16(left, right)
Definition iwram_call.h:127
#define CLAMP(v, low, high)
Keeps v within low to high, the low bound tested first.
Definition macros.h:23
u32 Random16(void)
Advances the game's random number generator (the C library's classic linear congruential one),...
Definition math.c:10
void Battle_SelectTerrainBackdrop(s32 behavior)
Chooses the battle backdrop for a world map terrain behavior (1 to 7; any other gives Sol Sanctum's) ...
Definition encounter.c:404
void Scene_ResolveInteractionResult(void)
Chooses the current scene's field music (gGameState.cue) from the first rule matching the scene or it...
Definition encounter.c:279
s32 Encounter_ResolveWorldMapZoneAndBackdrop(void *pos)
Returns the world map encounter zone for the terrain at pos (0 for none), and chooses the battle back...
Definition encounter.c:103
u16 Encounter_GetEnemyGroupAtSlot(s32 zone, s32 slot)
Returns the enemy group in slot (0 to 7) of zone; 14 halfwords make a zone.
Definition encounter.c:244
const struct SceneAreaNameRule gScene_AreaNameRules[]
s32 Encounter_SelectEnemyGroup(s32 zone, s32 steps)
Advances the encounter counter for zone and returns the enemy group to fight, or 0 for no battle.
Definition encounter.c:130
s16 Event_GetSpecialValue(void)
Returns the battle backdrop chosen for the current scene.
Definition encounter.c:375
s32 Encounter_RollWorldMap(s32 pos, s32 steps)
Rolls a random battle on the world map, taking the zone from the tile at pos.
Definition encounter.c:236
void BattleFx_SelectBattleCue(s32 zone, s32 slot)
Chooses the battle music for slot of zone (gGameState.sceneCue).
Definition encounter.c:439
void Encounter_SelectSceneZones(s32 useRules)
Chooses the current scene's encounter zones, one for each kind of ground the party can stand on (gEve...
Definition encounter.c:49
s32 Encounter_RollTerrain(u32 terrain, s32 steps)
Rolls a random battle on the encounter zone of the ground underfoot.
Definition encounter.c:226
void Battle_SelectSceneBackdrop(void)
Chooses the battle backdrop for the current scene.
Definition encounter.c:382
s16 Encounter_GetDjinnEnemyGroup(s32 djinni)
Returns the enemy group of Djinni battle djinni, which the table gives as a zone and slot.
Definition encounter.c:251
@ ENCOUNTER_GROUP_COUNT
Definition encounter.c:29
const struct BattleLocationRule gBattle_LocationRules[]
const struct SceneInteractionEntry gScene_InteractionRuleTable[]
void Scene_SetDefeatReturnPoint(void)
Sets the scene the party returns to after a defeat (gGameState.savedScene and savedEntrance),...
Definition encounter.c:357
s32 Scene_GetAreaNameIndex(s32 scene, s32 entrance)
Returns the place name of scene and entrance, from the first rule matching the scene or its group; 0 ...
Definition encounter.c:326
What every part of object.c shares: its includes and the macros and types more than one part uses.
char ResourceId_ForestBackdrop
char ResourceId_SolSanctumBackdrop
char ResourceId_SnowfieldBackdrop
char ResourceId_BeachBackdrop
char ResourceId_DesertBackdrop
char ResourceId_GrasslandBackdrop
s32 BattleFx_GetResourceGroup(s32 scene)
Returns the group scene belongs to.
Definition scene.c:156
#define MUS_018
Definition songs.h:28
struct EventWork * gEventWork
A field scene's encounter zones (gBattle_LocationRules, location_rules.s), ended by scene -1.
Definition encounter.c:9
s16 firstZone
the first of up to seven consecutive zones
Definition encounter.c:14
s16 entrance
-1 for any entrance
Definition encounter.c:11
s16 firstZoneOnGround
the first zone is for ground 0, not 1
Definition encounter.c:13
s16 flag
-1, or a flag that turns the rule off once set
Definition encounter.c:12
u32 experience
a character's experience points
An encounter zone, a row of encounter.tsv: its rate, the level it is made for, and the enemy groups t...
Definition encounter.c:33
u8 weights[ENCOUNTER_GROUP_COUNT]
each group's relative chance
Definition encounter.c:37
u16 enemies[ENCOUNTER_GROUP_COUNT]
the enemy groups that can appear
Definition encounter.c:36
u16 rate
the base encounter rate
Definition encounter.c:34
u16 level
the party level the zone is made for
Definition encounter.c:35
Full event work allocated by Field_RunScene (heap slot 27).
u8 encounterGroups[8]
encounter zone for each kind of ground
s32 encounterBias
randomized distance adjustment between battles
s32 encounterThreshold
encounter steps before a battle; 0x100000
s16 cloaked
1 under Cloak, which prevents battles
Definition game_state.h:114
s16 special
the battle backdrop chosen for the scene or terrain
Definition game_state.h:47
s16 sceneGroup
the current scene's group; a new group resets its flags
Definition game_state.h:42
s16 sceneCue
the battle music chosen for the encounter
Definition game_state.h:55
s32 avoidDistance
distance left of Avoid, 16.16 pixels; 0 when off
Definition game_state.h:110
s32 encounterSteps
the encounter count, reset with a new group
Definition game_state.h:105
s16 entrance
Definition game_state.h:37
s16 savedScene
where a defeat returns to without a defeat point
Definition game_state.h:38
s16 cue
the current scene's field music
Definition game_state.h:56
s16 savedEntrance
where a defeat returns to without a defeat point
Definition game_state.h:39
A rule naming the place a scene is in (gScene_AreaNameRules, scene_rules.s), ended by id -1.
Definition encounter.c:312
s16 entrance
-1 for any entrance
Definition encounter.c:314
s32 areaNameIndex
the place name
Definition encounter.c:316
s16 id
a scene, or a scene group
Definition encounter.c:313
u16 useSceneId
1 when id is a scene, 0 when it is a group
Definition encounter.c:315
A rule choosing a scene's field music (gScene_InteractionRuleTable, scene_rules.s),...
Definition encounter.c:265
s16 flag
-1, or a flag that must be set
Definition encounter.c:269
u16 idIsScene
1 when id is a scene, 0 when it is a group
Definition encounter.c:268
s16 id
a scene, or a scene group
Definition encounter.c:266
s16 entrance
-1 for any entrance
Definition encounter.c:267
The world map's encounter zone for each kind of terrain (gWorldMap_EncounterRules,...
Definition encounter.c:21
s16 flag
-1, or a flag that turns the rule off once set
Definition encounter.c:24
s16 kind
the terrain kind of the map block
Definition encounter.c:22
s16 behavior
the terrain's behavior, or -1 for any
Definition encounter.c:23
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
const u16 gEncounter_AreaEntryTable[160]
The encounter of each Djinni battle (Encounter_GetDjinnEnemyGroup): {zone, slot}.
Definition zonedata.c:19
const s16 gWorldMap_EncounterRules[132]
The world map's encounter zones (struct TerrainRule): {terrain kind, terrain behavior (-1 for any),...
Definition zonedata.c:7